You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@velocity.apache.org by jo...@locus.apache.org on 2000/11/04 00:26:53 UTC

cvs commit: jakarta-velocity/src/java/org/apache/velocity/test/misc Test.java

jon         00/11/03 15:26:53

  Modified:    src/java/org/apache/velocity/runtime/defaults
                        velocity.properties
               src/java/org/apache/velocity/servlet VelocityServlet.java
               src/java/org/apache/velocity/test TemplateTestCase.java
               src/java/org/apache/velocity/test/misc Test.java
  Added:       src/java/org/apache/velocity/io ArrayByteData.java
                        ByteBuffer.java ByteBufferOutputStream.java
                        ByteData.java CharToByteBuffer.java
                        CharToByteBufferWriter.java DefaultByteBuffer.java
                        DefaultCharToByteBuffer.java FileByteBuffer.java
                        FileByteData.java IdentityMap.java
                        InternedCharToByteBuffer.java
                        SpilloverByteBuffer.java TemporaryFile.java
  Removed:     src/java/org/apache/velocity/io FastWriter.java
  Log:
  removed FastWriter and replaced it with TeaServlet's excellent IO code
  
  Revision  Changes    Path
  1.1                  jakarta-velocity/src/java/org/apache/velocity/io/ArrayByteData.java
  
  Index: ArrayByteData.java
  ===================================================================
  package org.apache.velocity.io;
  
  /* ====================================================================
   * TeaServlet - Copyright (c) 1999-2000 Walt Disney Internet Group
   * ====================================================================
   * The Tea Software License, Version 1.1
   *
   * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Walt Disney Internet Group (http://opensource.go.com/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact opensource@dig.com.
   *
   * 5. Products derived from this software may not be called "Tea",
   *    "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
   *    "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
   *    written permission of the Walt Disney Internet Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS
   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   * ====================================================================
   *
   * For more information about Tea, please see http://opensource.go.com/.
   */
  
  
  import java.io.OutputStream;
  import java.io.IOException;
  
  /******************************************************************************
   * A ByteData implementation that wraps an array of bytes.
   * 
   * @author Brian S O'Neill
   * @version
   * <!--$$Revision: 1.1 $-->, <!--$$JustDate:-->  9/07/00 <!-- $-->
   */
  public class ArrayByteData implements ByteData {
      private byte[] mData;
      private int mOffset;
      private int mLength;
  
      public ArrayByteData(byte[] data) {
          this(data, 0, data.length);
      }
  
      public ArrayByteData(byte[] data, int offset, int length) {
          mData = data;
          mOffset = offset;
          mLength = length;
      }
  
      public long getByteCount() {
          return mLength;
      }
  
      public void writeTo(OutputStream out) throws IOException {
          out.write(mData, mOffset, mLength);
      }
  
      public void reset() {
          // No transient data to reset.
      }
  }
  
  
  
  1.1                  jakarta-velocity/src/java/org/apache/velocity/io/ByteBuffer.java
  
  Index: ByteBuffer.java
  ===================================================================
  package org.apache.velocity.io;
  
  /* ====================================================================
   * TeaServlet - Copyright (c) 1999-2000 Walt Disney Internet Group
   * ====================================================================
   * The Tea Software License, Version 1.1
   *
   * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Walt Disney Internet Group (http://opensource.go.com/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact opensource@dig.com.
   *
   * 5. Products derived from this software may not be called "Tea",
   *    "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
   *    "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
   *    written permission of the Walt Disney Internet Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS
   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   * ====================================================================
   *
   * For more information about Tea, please see http://opensource.go.com/.
   */
  
  import java.io.IOException;
  
  /******************************************************************************
   * ByteBuffer extends ByteData such that bytes can be added to it.
   *
   * @author Brian S O'Neill
   * @version
   * <!--$$Revision: 1.1 $-->, <!--$$JustDate:-->  9/07/00 <!-- $-->
   */
  public interface ByteBuffer extends ByteData {
      /**
       * Returns the base byte count, which excludes surrogates.
       */
      public long getBaseByteCount() throws IOException;
  
      /**
       * Add one byte to the end of this buffer.
       */
      public void append(byte b) throws IOException;
  
      /**
       * Copy the given bytes to the end of this buffer.
       */
      public void append(byte[] bytes) throws IOException;
  
      /**
       * Copy the given bytes to the end of this buffer, starting at the offset,
       * using the length provided.
       */
      public void append(byte[] bytes, int offset, int length)
          throws IOException;
  
      /**
       * Append ByteData that will not be touched until this ByteBuffer needs
       * to calculate its byte count, or it needs to write out. A null surrogate
       * is not appended.
       */
      public void appendSurrogate(ByteData s) throws IOException;
  
      /**
       * Add a ByteBuffer that will receive a copy of all the data appended to
       * this ByteBuffer.
       */
      public void addCaptureBuffer(ByteBuffer buffer) throws IOException;
  
      /**
       * Remove a capture buffer that was previously added by addCaptureBuffer.
       */
      public void removeCaptureBuffer(ByteBuffer buffer) throws IOException;
  }
  
  
  
  1.1                  jakarta-velocity/src/java/org/apache/velocity/io/ByteBufferOutputStream.java
  
  Index: ByteBufferOutputStream.java
  ===================================================================
  package org.apache.velocity.io;
  
  /* ====================================================================
   * TeaServlet - Copyright (c) 1999-2000 Walt Disney Internet Group
   * ====================================================================
   * The Tea Software License, Version 1.1
   *
   * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Walt Disney Internet Group (http://opensource.go.com/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact opensource@dig.com.
   *
   * 5. Products derived from this software may not be called "Tea",
   *    "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
   *    "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
   *    written permission of the Walt Disney Internet Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS
   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   * ====================================================================
   *
   * For more information about Tea, please see http://opensource.go.com/.
   */
  
  import java.io.OutputStream;
  import java.io.IOException;
  
  /******************************************************************************
   * An OutputStream that writes into a ByteBuffer.
   *
   * @author Brian S O'Neill
   * @version
   * <!--$$Revision: 1.1 $-->, <!--$$JustDate:-->  9/07/00 <!-- $-->
   */
  public class ByteBufferOutputStream extends OutputStream {
      private ByteBuffer mBuffer;
      private boolean mClosed;
  
      public ByteBufferOutputStream(ByteBuffer buffer) {
          mBuffer = buffer;
      }
  
      public void write(int b) throws IOException {
          checkIfClosed();
          mBuffer.append((byte)b);
      }
  
      public void write(byte[] bytes) throws IOException {
          checkIfClosed();
          mBuffer.append(bytes);
      }
  
      public void write(byte[] bytes, int offset, int length)
          throws IOException {
          checkIfClosed();
          mBuffer.append(bytes, offset, length);
      }
  
      public void flush() throws IOException {
          checkIfClosed();
      }
  
      public void close() {
          mClosed = true;
      }
  
      private void checkIfClosed() throws IOException {
          if (mClosed) {
              throw new IOException("OutputStream closed");
          }
      }
  }
  
  
  
  1.1                  jakarta-velocity/src/java/org/apache/velocity/io/ByteData.java
  
  Index: ByteData.java
  ===================================================================
  package org.apache.velocity.io;
  
  /* ====================================================================
   * TeaServlet - Copyright (c) 1999-2000 Walt Disney Internet Group
   * ====================================================================
   * The Tea Software License, Version 1.1
   *
   * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Walt Disney Internet Group (http://opensource.go.com/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact opensource@dig.com.
   *
   * 5. Products derived from this software may not be called "Tea",
   *    "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
   *    "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
   *    written permission of the Walt Disney Internet Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS
   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   * ====================================================================
   *
   * For more information about Tea, please see http://opensource.go.com/.
   */
  
  import java.io.OutputStream;
  import java.io.IOException;
  
  /******************************************************************************
   * Simple interface for writing a list of bytes to an OutputStream.
   *
   * @author Brian S O'Neill
   * @version
   * <!--$$Revision: 1.1 $-->, <!--$$JustDate:-->  9/07/00 <!-- $-->
   */
  public interface ByteData {
      /**
       * Return the amount of bytes that will be written by the writeTo method.
       */
      public long getByteCount() throws IOException;
  
      /**
       * Writes all the bytes to the given OutputStream.
       */
      public void writeTo(OutputStream out) throws IOException;
  
      /**
       * Reset any transient data stored in this ByteData. A call to getByteCount
       * or writeTo will force this data to be restored.
       */
      public void reset() throws IOException;
  }
  
  
  
  1.1                  jakarta-velocity/src/java/org/apache/velocity/io/CharToByteBuffer.java
  
  Index: CharToByteBuffer.java
  ===================================================================
  package org.apache.velocity.io;
  
  /* ====================================================================
   * TeaServlet - Copyright (c) 1999-2000 Walt Disney Internet Group
   * ====================================================================
   * The Tea Software License, Version 1.1
   *
   * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Walt Disney Internet Group (http://opensource.go.com/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact opensource@dig.com.
   *
   * 5. Products derived from this software may not be called "Tea",
   *    "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
   *    "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
   *    written permission of the Walt Disney Internet Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS
   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   * ====================================================================
   *
   * For more information about Tea, please see http://opensource.go.com/.
   */
  
  import java.io.IOException;
  import java.io.UnsupportedEncodingException;
  
  /******************************************************************************
   * A ByteBuffer that accepts characters and Strings as well.
   *
   * @author Brian S O'Neill
   * @version
   * <!--$$Revision: 1.1 $-->, <!--$$JustDate:-->  9/07/00 <!-- $-->
   */
  public interface CharToByteBuffer extends ByteBuffer {
      /**
       * Set the encoding for converting characters to bytes. Calling getEncoding
       * will return the canonical encoding name and may differ from the
       * encoding name provided to this method.
       */
      public void setEncoding(String enc)
          throws IOException, UnsupportedEncodingException;
  
      /**
       * Returns the current encoding that is being used to convert characters
       * to bytes or null if no encoding has been set yet. The encoding name
       * that is returned is canonical and may differ from the name passed into
       * setEncoding.
       */
      public String getEncoding() throws IOException;
  
      /**
       * Add one character to the end of this buffer.
       */
      public void append(char c) throws IOException;
  
      /**
       * Copy the given characters to the end of this buffer.
       */
      public void append(char[] chars) throws IOException;
  
      /**
       * Copy the given characters to the end of this buffer, starting at the
       * offset, using the length provided.
       */
      public void append(char[] chars, int offset, int length)
          throws IOException;
  
      /**
       * Copy the given String to the end of this buffer.
       */
      public void append(String str) throws IOException;
  
      /**
       * Copy the given String to the end of this buffer, starting at the offset,
       * using the length provided.
       */
      public void append(String str, int offset, int length) throws IOException;
  }
  
  
  
  1.1                  jakarta-velocity/src/java/org/apache/velocity/io/CharToByteBufferWriter.java
  
  Index: CharToByteBufferWriter.java
  ===================================================================
  package org.apache.velocity.io;
  
  /* ====================================================================
   * TeaServlet - Copyright (c) 1999-2000 Walt Disney Internet Group
   * ====================================================================
   * The Tea Software License, Version 1.1
   *
   * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Walt Disney Internet Group (http://opensource.go.com/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact opensource@dig.com.
   *
   * 5. Products derived from this software may not be called "Tea",
   *    "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
   *    "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
   *    written permission of the Walt Disney Internet Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS
   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   * ====================================================================
   *
   * For more information about Tea, please see http://opensource.go.com/.
   */
  
  import java.io.Writer;
  import java.io.IOException;
  
  /******************************************************************************
   * A Writer that writes into a CharToByteBuffer.
   * 
   * @author Brian S O'Neill
   * @version
   * <!--$$Revision: 1.1 $-->, <!--$$JustDate:-->  9/07/00 <!-- $-->
   */
  public class CharToByteBufferWriter extends Writer {
      private CharToByteBuffer mBuffer;
      private boolean mClosed;
  
      public CharToByteBufferWriter(CharToByteBuffer buffer) {
          mBuffer = buffer;
      }
  
      public void write(int c) throws IOException {
          checkIfClosed();
          mBuffer.append((char)c);
      }
  
      public void write(char[] chars) throws IOException {
          checkIfClosed();
          mBuffer.append(chars);
      }
  
      public void write(char[] chars, int offset, int length) 
          throws IOException {
          checkIfClosed();
          mBuffer.append(chars, offset, length);
      }
  
      public void write(String str) throws IOException {
          checkIfClosed();
          mBuffer.append(str);
      }
  
      public void write(String str, int offset, int length) throws IOException {
          checkIfClosed();
          mBuffer.append(str, offset, length);
      }
  
      public void flush() throws IOException {
          checkIfClosed();
      }
  
      public void close() {
          mClosed = true;
      }
  
      private void checkIfClosed() throws IOException {
          if (mClosed) {
              throw new IOException("Writer closed");
          }
      }
  }
  
  
  
  1.1                  jakarta-velocity/src/java/org/apache/velocity/io/DefaultByteBuffer.java
  
  Index: DefaultByteBuffer.java
  ===================================================================
  package org.apache.velocity.io;
  
  /* ====================================================================
   * TeaServlet - Copyright (c) 1999-2000 Walt Disney Internet Group
   * ====================================================================
   * The Tea Software License, Version 1.1
   *
   * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Walt Disney Internet Group (http://opensource.go.com/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact opensource@dig.com.
   *
   * 5. Products derived from this software may not be called "Tea",
   *    "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
   *    "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
   *    written permission of the Walt Disney Internet Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS
   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   * ====================================================================
   *
   * For more information about Tea, please see http://opensource.go.com/.
   */
  
  import java.io.OutputStream;
  import java.io.IOException;
  import java.util.List;
  import java.util.ArrayList;
  
  /******************************************************************************
   * A ByteBuffer implementation that keeps byte data in memory.
   *
   * @author Brian S O'Neill
   * @version
   * <!--$$Revision: 1.1 $-->, <!--$$JustDate:-->  9/07/00 <!-- $-->
   */
  public class DefaultByteBuffer implements ByteBuffer {
      private static final int BUFFER_SIZE = 512;
  
      // A List of ByteData instances.
      private List mChunks;
  
      private byte[] mBuffer;
      private int mCursor;
  
      private int mBaseCount;
  
      private List mCaptureBuffers;
  
      public DefaultByteBuffer() {
          mChunks = new ArrayList(100);
      }
  
      public long getBaseByteCount() {
          if (mBuffer != null) {
              return mBaseCount + mCursor;
          }
          else {
              return mBaseCount;
          }
      }
  
      public long getByteCount() throws IOException {
          long count;
          if (mBuffer != null) {
              count = mCursor;
          }
          else {
              count = 0;
          }
  
          int size = mChunks.size();
          for (int i=0; i<size; i++) {
              count += ((ByteData)mChunks.get(i)).getByteCount();
          }
  
          return count;
      }
  
      public void writeTo(OutputStream out) throws IOException {
          int size = mChunks.size();
          for (int i=0; i<size; i++) {
              ((ByteData)mChunks.get(i)).writeTo(out);
          }
  
          if (mBuffer != null && mCursor != 0) {
              out.write(mBuffer, 0, mCursor);
          }
      }
  
      public void append(byte b) throws IOException {
          List captureBuffers;
          if ((captureBuffers = mCaptureBuffers) != null) {
              int size = captureBuffers.size();
              for (int i=0; i<size; i++) {
                  ((ByteBuffer)captureBuffers.get(i)).append(b);
              }
          }
  
          if (mBuffer == null) {
              mBuffer = new byte[BUFFER_SIZE];
              mCursor = 0;
          }
          else if (mCursor >= mBuffer.length) {
              mChunks.add(new ArrayByteData(mBuffer));
              mBaseCount += BUFFER_SIZE;
              mBuffer = new byte[BUFFER_SIZE];
              mCursor = 0;
          }
  
          mBuffer[mCursor++] = b;
      }
  
      public void append(byte[] bytes) throws IOException {
          append(bytes, 0, bytes.length);
      }
  
      public void append(byte[] bytes, int offset, int length)
          throws IOException
      {
          List captureBuffers;
          if ((captureBuffers = mCaptureBuffers) != null) {
              int size = captureBuffers.size();
              for (int i=0; i<size; i++) {
                  ((ByteBuffer)captureBuffers.get(i)).append
                      (bytes, offset, length);
              }
          }
  
          while (length > 0) {
              if (mBuffer == null) {
                  if (length >= BUFFER_SIZE) {
                      byte[] copy = new byte[length];
                      System.arraycopy(bytes, offset, copy, 0, length);
                      mChunks.add(new ArrayByteData(copy));
                      mBaseCount += length;
                      return;
                  }
                  
                  mBuffer = new byte[BUFFER_SIZE];
                  mCursor = 0;
              }
              
              int available = BUFFER_SIZE - mCursor;
              
              if (length <= available) {
                  System.arraycopy(bytes, offset, mBuffer, mCursor, length);
                  mCursor += length;
                  return;
              }
              
              System.arraycopy(bytes, offset, mBuffer, mCursor, available);
              mChunks.add(new ArrayByteData(mBuffer));
              mBaseCount += BUFFER_SIZE;
              mBuffer = null;
              offset += available;
              length -= available;
          }
      }
  
      public void appendSurrogate(ByteData s) throws IOException {
          if (s == null) {
              return;
          }
  
          List captureBuffers;
          if ((captureBuffers = mCaptureBuffers) != null) {
              int size = captureBuffers.size();
              for (int i=0; i<size; i++) {
                  ((ByteBuffer)captureBuffers.get(i)).appendSurrogate(s);
              }
          }
  
          if (mBuffer != null && mCursor > 0) {
              mChunks.add(new ArrayByteData(mBuffer, 0, mCursor));
              mBaseCount += mCursor;
              mBuffer = null;
          }
          mChunks.add(s);
      }
  
      public void addCaptureBuffer(ByteBuffer buffer) {
          List captureBuffers;
          if ((captureBuffers = mCaptureBuffers) == null) {
              captureBuffers = mCaptureBuffers = new ArrayList();
          }
          captureBuffers.add(buffer);
      }
  
      public void removeCaptureBuffer(ByteBuffer buffer) {
          List captureBuffers;
          if ((captureBuffers = mCaptureBuffers) != null) {
              captureBuffers.remove(buffer);
          }
      }
  
      public void reset() throws IOException {
          int size = mChunks.size();
          for (int i=0; i<size; i++) {
              ((ByteData)mChunks.get(i)).reset();
          }
  
          List captureBuffers;
          if ((captureBuffers = mCaptureBuffers) != null) {
              size = captureBuffers.size();
              for (int i=0; i<size; i++) {
                  ((ByteData)captureBuffers.get(i)).reset();
              }
          }
      }
  }
  
  
  
  1.1                  jakarta-velocity/src/java/org/apache/velocity/io/DefaultCharToByteBuffer.java
  
  Index: DefaultCharToByteBuffer.java
  ===================================================================
  package org.apache.velocity.io;
  
  /* ====================================================================
   * TeaServlet - Copyright (c) 1999-2000 Walt Disney Internet Group
   * ====================================================================
   * The Tea Software License, Version 1.1
   *
   * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Walt Disney Internet Group (http://opensource.go.com/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact opensource@dig.com.
   *
   * 5. Products derived from this software may not be called "Tea",
   *    "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
   *    "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
   *    written permission of the Walt Disney Internet Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS
   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   * ====================================================================
   *
   * For more information about Tea, please see http://opensource.go.com/.
   */
  
  import java.io.*;
  
  /******************************************************************************
   * A CharToByteBuffer implementation that wraps a ByteBuffer for storage.
   * 
   * @author Brian S O'Neill
   * @version
   * <!--$$Revision: 1.1 $-->, <!--$$JustDate:-->  9/07/00 <!-- $-->
   */
  public class DefaultCharToByteBuffer implements CharToByteBuffer {
      private ByteBuffer mBuffer;
      private OutputStreamWriter mConvertor;
  
      private char[] mChars;
      private int mCapacity;
      private int mCursor;
      
      private String mDefaultEncoding;
  
      /**
       * @param buffer Buffer that receives the characters converted to bytes.
       */    
      public DefaultCharToByteBuffer(ByteBuffer buffer) {
          this(buffer, null);
      }
  
      /**
       * @param buffer Buffer that receives the characters converted to bytes.
       * @param defaultEncoding Default character encoding to use if setEncoding
       * is not called.
       */    
      public DefaultCharToByteBuffer(ByteBuffer buffer, String defaultEncoding) {
          mBuffer = buffer;
          mChars = new char[4000];
          mCapacity = mChars.length;
          mDefaultEncoding = defaultEncoding;
      }
  
      public void setEncoding(String enc) throws IOException {
          drain(true);
          mConvertor = new OutputStreamWriter
              (new ByteBufferOutputStream(mBuffer), enc);
      }
      
      public String getEncoding() {
          return (mConvertor == null) ? mDefaultEncoding :
              mConvertor.getEncoding();
      }
      
      public long getBaseByteCount() throws IOException {
          return mBuffer.getBaseByteCount();
      }
  
      public long getByteCount() throws IOException {
          drain(true);
          return mBuffer.getByteCount();
      }
      
      public void writeTo(OutputStream out) throws IOException {
          drain(true);
          mBuffer.writeTo(out);
      }
      
      public void append(byte b) throws IOException {
          drain(true);
          mBuffer.append(b);
      }
      
      public void append(byte[] bytes) throws IOException {
          append(bytes, 0, bytes.length);
      }
      
      public void append(byte[] bytes, int offset, int length)
          throws IOException {
  
          if (length != 0) {
              drain(true);
              mBuffer.append(bytes, offset, length);
          }
      }
      
      public void appendSurrogate(ByteData s) throws IOException {
          if (s != null) {
              drain(true);
              mBuffer.appendSurrogate(s);
          }
      }
      
      public void addCaptureBuffer(ByteBuffer buffer) throws IOException {
          drain(true);
          mBuffer.addCaptureBuffer(buffer);
      }
  
      public void removeCaptureBuffer(ByteBuffer buffer) throws IOException {
          drain(true);
          mBuffer.removeCaptureBuffer(buffer);
      }
  
      public void append(char c) throws IOException {
          if (mCursor >= mCapacity) {
              drain(false);
          }
          mChars[mCursor++] = c;
      }
      
      public void append(char[] chars) throws IOException {
          append(chars, 0, chars.length);
      }
      
      public void append(char[] chars, int offset, int length) 
          throws IOException 
      {
          if (length == 0) {
              return;
          }
  
          int capacity = mCapacity;
  
          if (length < (capacity - mCursor)) {
              System.arraycopy(chars, offset, mChars, mCursor, length);
              mCursor += length;
              return;
          }
  
          // Make room and try again.
          drain(false);
  
          if (length < capacity) {
              System.arraycopy(chars, offset, mChars, mCursor, length);
              mCursor += length;
              return;
          }
  
          // Write the whole chunk out at once.
          getConvertor().write(chars, offset, length);
      }
      
      public void append(String str) throws IOException {
          append(str, 0, str.length());
      }
      
      public void append(String str, int offset, int length) throws IOException {
          if (length == 0) {
              return;
          }
  
          int capacity = mCapacity;
          int avail = capacity - mCursor;
  
          if (length <= avail) {
              str.getChars(offset, offset + length, mChars, mCursor);
              mCursor += length;
              return;
          }
  
          // Fill up the rest of the character buffer and drain it.
          str.getChars(offset, offset + avail, mChars, mCursor);
          offset += avail;
          length -= avail;
          mCursor = capacity;
          drain(false);
  
          // Drain chunks that completely fill the character buffer.
          while (length >= capacity) {
              str.getChars(offset, offset + capacity, mChars, 0);
              offset += capacity;
              length -= capacity;
              mCursor = capacity;
              drain(false);
          }
  
          // Copy the remainder into the character buffer, but don't drain.
          if (length > 0) {
              str.getChars(offset, offset + length, mChars, 0);
              mCursor = length;
          }
      }
  
      public void reset() throws IOException {
          mBuffer.reset();
      }
  
      private OutputStreamWriter getConvertor()
          throws UnsupportedEncodingException
      {
          if (mConvertor == null) {
              if (mDefaultEncoding == null) {
                  mConvertor = new OutputStreamWriter
                      (new ByteBufferOutputStream(mBuffer));
              }
              else {
                  mConvertor = new OutputStreamWriter
                      (new ByteBufferOutputStream(mBuffer), mDefaultEncoding);
              }
          }
          return mConvertor;
      }
  
      private void drain(boolean flush) throws IOException {
          if (mCursor != 0) {
              try {
                  getConvertor().write(mChars, 0, mCursor);
              }
              finally {
                  mCursor = 0;
              }
          }
  
          if (flush && mConvertor != null) {
              mConvertor.flush();
          }
      }
  }
  
  
  
  1.1                  jakarta-velocity/src/java/org/apache/velocity/io/FileByteBuffer.java
  
  Index: FileByteBuffer.java
  ===================================================================
  package org.apache.velocity.io;
  
  /* ====================================================================
   * TeaServlet - Copyright (c) 1999-2000 Walt Disney Internet Group
   * ====================================================================
   * The Tea Software License, Version 1.1
   *
   * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Walt Disney Internet Group (http://opensource.go.com/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact opensource@dig.com.
   *
   * 5. Products derived from this software may not be called "Tea",
   *    "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
   *    "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
   *    written permission of the Walt Disney Internet Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS
   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   * ====================================================================
   *
   * For more information about Tea, please see http://opensource.go.com/.
   */
  
  import java.io.OutputStream;
  import java.io.RandomAccessFile;
  import java.io.IOException;
  import java.util.List;
  import java.util.ArrayList;
  
  /******************************************************************************
   * A ByteBuffer implementation that can read from an open file or can write
   * to it. This implementation is best suited for temporary byte data that is
   * too large to hold in memory.
   *
   * @author Brian S O'Neill
   * @version
   * <!--$$Revision: 1.1 $--> 5 <!-- $$JustDate:-->  9/07/00 <!-- $-->
   */
  public class FileByteBuffer implements ByteBuffer {
      private RandomAccessFile mFile;
      private List mSurrogates;
      private List mCaptureBuffers;
  
      /**
       * Creates a FileByteBuffer on a RandomAccessFile. If the file is opened
       * read-only, then the append operations will fail.
       *
       * @param file The file to use as a buffer.
       */
      public FileByteBuffer(RandomAccessFile file) throws IOException {
          mFile = file;
          file.seek(0);
      }
  
      public long getBaseByteCount() throws IOException {
          return mFile.length();
      }
  
      public long getByteCount() throws IOException {
          long count = getBaseByteCount();
          if (mSurrogates == null) {
              return count;
          }
          
          int size = mSurrogates.size();
          for (int i=0; i<size; i++) {
              count += ((Surrogate)mSurrogates.get(i)).mByteData.getByteCount();
          }
  
          return count;
      }
  
      public void writeTo(OutputStream out) throws IOException {
          long length = mFile.length();
          int bufSize;
          if (length > 4000) {
              bufSize = 4000;
          }
          else {
              bufSize = (int)length;
          }
          
          byte[] inputBuffer = new byte[bufSize];
          
          mFile.seek(0);
  
          if (mSurrogates != null) {
              long currentPos = 0;
              
              int size = mSurrogates.size();
              for (int i=0; i<size; i++) {
                  Surrogate s = (Surrogate)mSurrogates.get(i);
                  currentPos = writeTo(inputBuffer, out, currentPos, s.mPos);
                  s.mByteData.writeTo(out);
              }
          }
  
          // Write out the rest of the file.
          int readAmount;
          while ((readAmount = mFile.read(inputBuffer, 0, bufSize)) > 0) {
              out.write(inputBuffer, 0, readAmount);
          }
      }
  
      private long writeTo(byte[] inputBuffer, OutputStream out, 
                           long fromPos, long toPos) throws IOException {
          if (toPos == fromPos) {
              return fromPos;
          }
  
          int bufSize = inputBuffer.length;
          int readAmount;
  
          while (toPos > fromPos) {
              int amount;
              if (bufSize <= (toPos - fromPos)) {
                  amount = bufSize;
              }
              else {
                  amount = (int)(toPos - fromPos);
              }
  
              while ((readAmount = mFile.read(inputBuffer, 0, amount)) > 0) {
                  out.write(inputBuffer, 0, readAmount);
                  fromPos += readAmount;
                  amount -= readAmount;
                  if (amount <= 0) {
                      break;
                  }
              }
  
              if (readAmount <= 0) {
                  break;
              }
          }
  
          return fromPos;
      }
  
      public void append(byte b) throws IOException {
          List captureBuffers;
          if ((captureBuffers = mCaptureBuffers) != null) {
              int size = captureBuffers.size();
              for (int i=0; i<size; i++) {
                  ((ByteBuffer)captureBuffers.get(i)).append(b);
              }
          }
  
          mFile.write(b);
      }
  
      public void append(byte[] bytes) throws IOException {
          mFile.write(bytes);
      }
  
      public void append(byte[] bytes, int offset, int length) 
          throws IOException 
      {
          List captureBuffers;
          if ((captureBuffers = mCaptureBuffers) != null) {
              int size = captureBuffers.size();
              for (int i=0; i<size; i++) {
                  ((ByteBuffer)captureBuffers.get(i)).append
                      (bytes, offset, length);
              }
          }
  
          mFile.write(bytes, offset, length); 
      }
  
      public void appendSurrogate(ByteData s) throws IOException {
          if (s == null) {
              return;
          }
  
          List captureBuffers;
          if ((captureBuffers = mCaptureBuffers) != null) {
              int size = captureBuffers.size();
              for (int i=0; i<size; i++) {
                  ((ByteBuffer)captureBuffers.get(i)).appendSurrogate(s);
              }
          }
  
          if (mSurrogates == null) {
              mSurrogates = new ArrayList();
          }
  
          mSurrogates.add(new Surrogate(s));
      }
  
      public void addCaptureBuffer(ByteBuffer buffer) {
          List captureBuffers;
          if ((captureBuffers = mCaptureBuffers) == null) {
              captureBuffers = mCaptureBuffers = new ArrayList();
          }
          captureBuffers.add(buffer);
      }
  
      public void removeCaptureBuffer(ByteBuffer buffer) {
          List captureBuffers;
          if ((captureBuffers = mCaptureBuffers) != null) {
              captureBuffers.remove(buffer);
          }
      }
  
      public void reset() throws IOException {
          List byteDatas;
          int i, size;
  
          if ((byteDatas = mSurrogates) != null) {
              size = byteDatas.size();
              for (i=0; i<size; i++) {
                  ((ByteData)byteDatas.get(i)).reset();
              }
          }
  
          if ((byteDatas = mCaptureBuffers) != null) {
              size = byteDatas.size();
              for (i=0; i<size; i++) {
                  ((ByteData)byteDatas.get(i)).reset();
              }
          }
      }
  
      private class Surrogate {
          public final ByteData mByteData;
          public final long mPos;
  
          public Surrogate(ByteData data) throws IOException {
              mByteData = data;
              mPos = mFile.getFilePointer();
          }
      }
  }
  
  
  
  1.1                  jakarta-velocity/src/java/org/apache/velocity/io/FileByteData.java
  
  Index: FileByteData.java
  ===================================================================
  package org.apache.velocity.io;
  
  /* ====================================================================
   * TeaServlet - Copyright (c) 1999-2000 Walt Disney Internet Group
   * ====================================================================
   * The Tea Software License, Version 1.1
   *
   * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Walt Disney Internet Group (http://opensource.go.com/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact opensource@dig.com.
   *
   * 5. Products derived from this software may not be called "Tea",
   *    "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
   *    "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
   *    written permission of the Walt Disney Internet Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS
   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   * ====================================================================
   *
   * For more information about Tea, please see http://opensource.go.com/.
   */
  
  import java.io.OutputStream;
  import java.io.File;
  import java.io.RandomAccessFile;
  import java.io.IOException;
  
  /******************************************************************************
   * A ByteData implementation that reads the contents of a file.
   *
   * @author Brian S O'Neill
   * @version
   * <!--$$Revision: 1.1 $-->, <!--$$JustDate:-->  9/07/00 <!-- $-->
   */
  public class FileByteData implements ByteData {
      private File mFile;
      private boolean mTriedToOpen;
      private RandomAccessFile mRAF;
  
      public FileByteData(File file) {
          mFile = file;
          // Open here so that if file isn't found, error is logged earlier.
          open();
      }
  
      public long getByteCount() throws IOException {
          // Keep file open to ensure that length doesn't change between call to
          // getByteCount and writeTo.
  
          RandomAccessFile raf = open();
          if (raf == null) {
              return 0;
          }
          else {
              return raf.length();
          }
      }
  
      public void writeTo(OutputStream out) throws IOException {
          RandomAccessFile raf = open();
          if (raf == null) {
              return;
          }
  
          try {
              long length = raf.length();
              int bufSize;
              if (length > 4000) {
                  bufSize = 4000;
              }
              else {
                  bufSize = (int)length;
              }
              
              byte[] inputBuffer = new byte[bufSize];
  
              raf.seek(0);
          
              int readAmount;
              while ((readAmount = raf.read(inputBuffer, 0, bufSize)) > 0) {
                  out.write(inputBuffer, 0, readAmount);
              }
          }
          finally {
              try {
                  finalize();
              }
              catch (IOException e) {
              }
          }
      }
  
      public void reset() throws IOException {
          if (mRAF != null) {
              try {
                  mRAF.close();
              }
              finally {
                  mTriedToOpen = false;
                  mRAF = null;
              }
          }       
      }
  
      protected final void finalize() throws IOException {
          reset();
      }
  
      private RandomAccessFile open() {
          if (!mTriedToOpen) {
              mTriedToOpen = true;
              try {
                  mRAF = new RandomAccessFile(mFile, "r");
              }
              catch (IOException e) {
                  Thread t = Thread.currentThread();
                  t.getThreadGroup().uncaughtException(t, e);
              }
          }
          return mRAF;
      }
  }
  
  
  
  1.1                  jakarta-velocity/src/java/org/apache/velocity/io/IdentityMap.java
  
  Index: IdentityMap.java
  ===================================================================
  package org.apache.velocity.io;
  
  /* ====================================================================
   * Trove - Copyright (c) 1997-2000 Walt Disney Internet Group
   * ====================================================================
   * The Tea Software License, Version 1.1
   *
   * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Walt Disney Internet Group (http://opensource.go.com/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact opensource@dig.com.
   *
   * 5. Products derived from this software may not be called "Tea",
   *    "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
   *    "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
   *    written permission of the Walt Disney Internet Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS
   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   * ====================================================================
   *
   * For more information about Tea, please see http://opensource.go.com/.
   */
  
  import java.lang.ref.*;
  import java.util.*;
  
  /******************************************************************************
   * An IdentityMap is like WeakHashMap, except it uses a key's identity
   * hashcode and equals methods. IdentityMap is not thread-safe and must be
   * wrapped with Collections.synchronizedMap to be made thread-safe. Most of the
   * implementation for this class is ripped off from java.util.HashMap, but not
   * java.util.WeakHashMap, in order to acheive greater efficiency.
   * <p>
   * The documentation for WeakHashMap states that it is intended primarily
   * for use with key objects whose equals methods test for object identity
   * using the == operator. Because WeakHashMap uses a key's own equals and
   * hashcode methods, it is better suited for implementing methods that behave
   * like {@link String#intern}. However, because WeakHashMap stongly references
   * values, {@link Utils#intern Utils.intern} provides a safer intern mechanism.
   * <p>
   * In this implementation, all key objects are tested for equality using the
   * == operator, and null keys are not permitted. IdentityMap is therefore
   * better suited for "canonicalized" mappings.
   * <p>
   * Note: Weakly referenced entries may be automatically removed during
   * either accessor or mutator operations, possibly causing a concurrent
   * modification to be detected. Therefore, even if multiple threads are only
   * accessing this map, be sure to synchronize this map first. Also, do not
   * rely on the value returned by size() when using an iterator from this map.
   * The iterators may return less entries than the amount reported by size().
   *
   * @author Brian S O'Neill
   * @version
   * <!--$$Revision: 1.1 $-->, <!--$$JustDate:-->  9/07/00 <!-- $-->
   * @see java.util.WeakHashMap
   * @see java.util.HashMap
   */
  public class IdentityMap extends AbstractMap implements Map, Cloneable {
      // Types of Iterators
      static final int KEYS = 0;
      static final int VALUES = 1;
      static final int ENTRIES = 2;
  
      static final Iterator cEmptyHashIterator = new Iterator() {
          public boolean hasNext() {
              return false;
          }
          
          public Object next() {
              throw new NoSuchElementException();
          }
          
          public void remove() {
              throw new IllegalStateException();
          }
      };
  
      /**
       * Test program.
       */
      /*
      public static void main(String[] args) throws Exception {
          Map map = new IdentityMap();
          map.put("Hello", "There");
          for (int i=0; i<1000000; i++) {
              if (i % 5 == 0) {
                  map.put(new String("Hello"), "Dude");
              }
              map.get("Hello");
              map.get("Stuff");
          }
  
          System.out.println(map.containsValue("Dude"));
          System.out.println(map.get("Hello"));
  
          System.gc();
  
          System.out.println(map);
          System.out.println(map.size());
  
          System.out.println(map.containsValue("Dude"));
          System.out.println(map.get("Hello"));
  
          map.remove("Hello");
  
          System.out.println(map);
          System.out.println(map.size());
  
          System.out.println(map.containsValue("Dude"));
          System.out.println(map.get("Hello"));
      }
      */
  
      /**
       * Converts a string to a collection without calling size(). Iterators from
       * this map may return less entries than the amount reported by size().
       */
      static String toString(Collection c) {
          StringBuffer buf = new StringBuffer();
          Iterator it = c.iterator();
          buf.append("[");
          for (int i = 0; it.hasNext(); i++) {
              if (i > 0) {
                  buf.append(", ");
              }
              buf.append(String.valueOf(it.next()));
          }
          buf.append("]");
          return buf.toString();
      }
  
      /**
       * The hash table data.
       */
      private transient Entry mTable[];
  
      /**
       * The total number of mappings in the hash table.
       */
      private transient int mCount;
  
      /**
       * The table is rehashed when its size exceeds this threshold.  (The
       * value of this field is (int)(capacity * loadFactor).)
       *
       * @serial
       */
      private int mThreshold;
  
      /**
       * The load factor for the hashtable.
       *
       * @serial
       */
      private float mLoadFactor;
  
      /**
       * The number of times this HashMap has been structurally modified
       * Structural modifications are those that change the number of mappings in
       * the HashMap or otherwise modify its internal structure (e.g.,
       * rehash).  This field is used to make iterators on Collection-views of
       * the HashMap fail-fast.  (See ConcurrentModificationException).
       */
      private transient int mModCount = 0;
  
      // Views
      
      private transient Set mKeySet = null;
      private transient Set mEntrySet = null;
      private transient Collection mValues = null;
  
      /**
       * Constructs a new, empty map with the specified initial 
       * capacity and the specified load factor. 
       *
       * @param      initialCapacity   the initial capacity of the HashMap.
       * @param      loadFactor        the load factor of the HashMap
       * @throws     IllegalArgumentException  if the initial capacity is less
       *               than zero, or if the load factor is nonpositive.
       */
      public IdentityMap(int initialCapacity, float loadFactor) {
          if (initialCapacity < 0) {
              throw new IllegalArgumentException("Illegal Initial Capacity: "+
                                                 initialCapacity);
          }
  
          if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
              throw new IllegalArgumentException("Illegal Load factor: "+
                                                 loadFactor);
          }
  
          if (initialCapacity == 0) {
              initialCapacity = 1;
          }
  
          mLoadFactor = loadFactor;
          mTable = new Entry[initialCapacity];
          mThreshold = (int)(initialCapacity * loadFactor);
      }
      
      /**
       * Constructs a new, empty map with the specified initial capacity
       * and default load factor, which is <tt>0.75</tt>.
       *
       * @param   initialCapacity   the initial capacity of the HashMap.
       * @throws    IllegalArgumentException if the initial capacity is less
       *              than zero.
       */
      public IdentityMap(int initialCapacity) {
          this(initialCapacity, 0.75f);
      }
  
      /**
       * Constructs a new, empty map with a default capacity and load
       * factor, which is <tt>0.75</tt>.
       */
      public IdentityMap() {
          this(11, 0.75f);
      }
  
      /**
       * Constructs a new map with the same mappings as the given map.  The
       * map is created with a capacity of twice the number of mappings in
       * the given map or 11 (whichever is greater), and a default load factor,
       * which is <tt>0.75</tt>.
       */
      public IdentityMap(Map t) {
          this(Math.max(2 * t.size(), 11), 0.75f);
          putAll(t);
      }
  
      /**
       * Returns the number of key-value mappings in this map, but this value
       * may be larger than actual amount of entries produced by an iterator.
       *
       * @return the number of key-value mappings in this map.
       */
      public int size() {
          return mCount;
      }
  
      /**
       * Returns <tt>true</tt> if this map contains no key-value mappings.
       *
       * @return <tt>true</tt> if this map contains no key-value mappings.
       */
      public boolean isEmpty() {
          return mCount == 0;
      }
  
      /**
       * Returns <tt>true</tt> if this map maps one or more keys to the
       * specified value.
       *
       * @param value value whose presence in this map is to be tested.
       * @return <tt>true</tt> if this map maps one or more keys to the
       *         specified value.
       */
      public boolean containsValue(Object value) {
          Entry tab[] = mTable;
          
          if (value == null) {
              for (int i = tab.length ; i-- > 0 ;) {
                  for (Entry e = tab[i], prev = null; e != null; e = e.mNext) {
                      if (e.getKey() == null) {
                          // Clean up after a cleared Reference.
                          mModCount++;
                          if (prev != null) {
                              prev.mNext = e.mNext;
                          }
                          else {
                              tab[i] = e.mNext;
                          }
                          mCount--;
                      }
                      else if (e.mValue == null) {
                          return true;
                      }
                      else {
                          prev = e;
                      }
                  }
              }
          }
          else {
              for (int i = tab.length ; i-- > 0 ;) {
                  for (Entry e = tab[i], prev = null; e != null; e = e.mNext) {
                      if (e.getKey() == null) {
                          // Clean up after a cleared Reference.
                          mModCount++;
                          if (prev != null) {
                              prev.mNext = e.mNext;
                          }
                          else {
                              tab[i] = e.mNext;
                          }
                          mCount--;
                      }
                      else if (value.equals(e.mValue)) {
                          return true;
                      }
                      else {
                          prev = e;
                      }
                  }
              }
          }
  
          return false;
      }
  
      /**
       * Returns <tt>true</tt> if this map contains a mapping for the specified
       * key.
       * 
       * @return <tt>true</tt> if this map contains a mapping for the specified
       * key.
       * @param key key whose presence in this Map is to be tested.
       */
      public boolean containsKey(Object key) {
          if (key == null) {
              return false;
          }
  
          Entry tab[] = mTable;
          int hash = System.identityHashCode(key);
          int index = (hash & 0x7FFFFFFF) % tab.length;
  
          for (Entry e = tab[index], prev = null; e != null; e = e.mNext) {
              Object entryKey = e.getKey();
  
              if (entryKey == null) {
                  // Clean up after a cleared Reference.
                  mModCount++;
                  if (prev != null) {
                      prev.mNext = e.mNext;
                  }
                  else {
                      tab[index] = e.mNext;
                  }
                  mCount--;
              }
              else if (e.mHash == hash && key == entryKey) {
                  return true;
              }
              else {
                  prev = e;
              }
          }
  
          return false;
      }
  
      /**
       * Returns the value to which this map maps the specified key.  Returns
       * <tt>null</tt> if the map contains no mapping for this key.  A return
       * value of <tt>null</tt> does not <i>necessarily</i> indicate that the
       * map contains no mapping for the key; it's also possible that the map
       * explicitly maps the key to <tt>null</tt>.  The <tt>containsKey</tt>
       * operation may be used to distinguish these two cases.
       *
       * @return the value to which this map maps the specified key.
       * @param key key whose associated value is to be returned.
       */
      public Object get(Object key) {
          if (key == null) {
              return null;
          }
  
          Entry tab[] = mTable;
          int hash = System.identityHashCode(key);
          int index = (hash & 0x7FFFFFFF) % tab.length;
  
          for (Entry e = tab[index], prev = null; e != null; e = e.mNext) {
              Object entryKey = e.getKey();
  
              if (entryKey == null) {
                  // Clean up after a cleared Reference.
                  mModCount++;
                  if (prev != null) {
                      prev.mNext = e.mNext;
                  }
                  else {
                      tab[index] = e.mNext;
                  }
                  mCount--;
              }
              else if (e.mHash == hash && key == entryKey) {
                  return e.mValue;
              }
              else {
                  prev = e;
              }
          }
  
          return null;
      }
  
      /**
       * Scans the contents of this map, removing all entries that have a
       * cleared weak key.
       */
      private void cleanup() {
          Entry tab[] = mTable;
          
          for (int i = tab.length ; i-- > 0 ;) {
              for (Entry e = tab[i], prev = null; e != null; e = e.mNext) {
                  if (e.getKey() == null) {
                      // Clean up after a cleared Reference.
                      mModCount++;
                      if (prev != null) {
                          prev.mNext = e.mNext;
                      }
                      else {
                          tab[i] = e.mNext;
                      }
                      mCount--;
                  }
                  else {
                      prev = e;
                  }
              }
          }
      }
  
      /**
       * Rehashes the contents of this map into a new <tt>HashMap</tt> instance
       * with a larger capacity. This method is called automatically when the
       * number of keys in this map exceeds its capacity and load factor.
       */
      private void rehash() {
          int oldCapacity = mTable.length;
          Entry oldMap[] = mTable;
          
          int newCapacity = oldCapacity * 2 + 1;
          Entry newMap[] = new Entry[newCapacity];
          
          mModCount++;
          mThreshold = (int)(newCapacity * mLoadFactor);
          mTable = newMap;
          
          for (int i = oldCapacity ; i-- > 0 ;) {
              for (Entry old = oldMap[i] ; old != null ; ) {
                  Entry e = old;
                  old = old.mNext;
  
                  // Only copy entry if its key hasn't been cleared.
                  if (e.getKey() == null) {
                      mCount--;
                  }
                  else {
                      int index = (e.mHash & 0x7FFFFFFF) % newCapacity;
                      e.mNext = newMap[index];
                      newMap[index] = e;
                  }
              }
          }
      }
      
      /**
       * Associates the specified value with the specified key in this map.
       * If the map previously contained a mapping for this key, the old
       * value is replaced.
       *
       * @param key key with which the specified value is to be associated.
       * @param value value to be associated with the specified key.
       * @return previous value associated with specified key, or <tt>null</tt>
       *         if there was no mapping for key.  A <tt>null</tt> return can
       *         also indicate that the HashMap previously associated
       *         <tt>null</tt> with the specified key.
       */
      public Object put(Object key, Object value) {
          if (key == null) {
              throw new NullPointerException("Null key is not permitted");
          }
  
          // Makes sure the key is not already in the HashMap.
          Entry tab[] = mTable;
          int hash = System.identityHashCode(key);
          int index = (hash & 0x7FFFFFFF) % tab.length;
  
          for (Entry e = tab[index], prev = null; e != null; e = e.mNext) {
              Object entryKey = e.getKey();
  
              if (entryKey == null) {
                  // Clean up after a cleared Reference.
                  mModCount++;
                  if (prev != null) {
                      prev.mNext = e.mNext;
                  }
                  else {
                      tab[index] = e.mNext;
                  }
                  mCount--;
              }
              else if (e.mHash == hash && key == entryKey) {
                  Object old = e.mValue;
                  e.mValue = value;
                  return old;
              }
              else {
                  prev = e;
              }
          }
  
          mModCount++;
  
          if (mCount >= mThreshold) {
              // Cleanup the table if the threshold is exceeded.
              cleanup();
          }
  
          if (mCount >= mThreshold) {
              // Rehash the table if the threshold is still exceeded.
              rehash();
              tab = mTable;
              index = (hash & 0x7FFFFFFF) % tab.length;
          }
          
          // Creates the new entry.
          Entry e = new Entry(hash, (Object)key, value, tab[index]);
          tab[index] = e;
          mCount++;
          return null;
      }
      
      /**
       * Removes the mapping for this key from this map if present.
       *
       * @param key key whose mapping is to be removed from the map.
       * @return previous value associated with specified key, or <tt>null</tt>
       *         if there was no mapping for key.  A <tt>null</tt> return can
       *         also indicate that the map previously associated <tt>null</tt>
       *         with the specified key.
       */
      public Object remove(Object key) {
          Entry tab[] = mTable;
          int hash = System.identityHashCode(key);
          int index = (hash & 0x7FFFFFFF) % tab.length;
              
          for (Entry e = tab[index], prev = null; e != null; e = e.mNext) {
              Object entryKey = e.getKey();
  
              if (entryKey == null) {
                  // Clean up after a cleared Reference.
                  mModCount++;
                  if (prev != null) {
                      prev.mNext = e.mNext;
                  }
                  else {
                      tab[index] = e.mNext;
                  }
                  mCount--;
              }
              else if (e.mHash == hash && key == entryKey) {
                  mModCount++;
                  if (prev != null) {
                      prev.mNext = e.mNext;
                  }
                  else {
                      tab[index] = e.mNext;
                  }
                  mCount--;
  
                  Object oldValue = e.mValue;
                  e.mValue = null;
                  return oldValue;
              }
              else {
                  prev = e;
              }
          }
  
          return null;
      }
      
      /**
       * Copies all of the mappings from the specified map to this one.
       * 
       * These mappings replace any mappings that this map had for any of the
       * keys currently in the specified Map.
       *
       * @param t Mappings to be stored in this map.
       */
      public void putAll(Map t) {
          Iterator i = t.entrySet().iterator();
          while (i.hasNext()) {
              Map.Entry e = (Map.Entry) i.next();
              put(e.getKey(), e.getValue());
          }
      }
  
      /**
       * Removes all mappings from this map.
       */
      public void clear() {
          Entry tab[] = mTable;
          mModCount++;
          for (int index = tab.length; --index >= 0; ) {
              tab[index] = null;
          }
          mCount = 0;
      }
  
      /**
       * Returns a shallow copy of this <tt>HashMap</tt> instance: the keys and
       * values themselves are not cloned.
       *
       * @return a shallow copy of this map.
       */
      public Object clone() {
          try { 
              IdentityMap t = (IdentityMap)super.clone();
              t.mTable = new Entry[mTable.length];
              for (int i = mTable.length ; i-- > 0 ; ) {
                  t.mTable[i] = (mTable[i] != null) 
                      ? (Entry)mTable[i].clone() : null;
              }
              t.mKeySet = null;
              t.mEntrySet = null;
              t.mValues = null;
              t.mModCount = 0;
              return t;
          }
          catch (CloneNotSupportedException e) { 
              // this shouldn't happen, since we are Cloneable
              throw new InternalError();
          }
      }
      
      /**
       * Returns a set view of the keys contained in this map.  The set is
       * backed by the map, so changes to the map are reflected in the set, and
       * vice-versa.  The set supports element removal, which removes the
       * corresponding mapping from this map, via the <tt>Iterator.remove</tt>,
       * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt>, and
       * <tt>clear</tt> operations.  It does not support the <tt>add</tt> or
       * <tt>addAll</tt> operations.
       *
       * @return a set view of the keys contained in this map.
       */
      public Set keySet() {
          if (mKeySet == null) {
              mKeySet = new AbstractSet() {
                  public Iterator iterator() {
                      return getHashIterator(KEYS);
                  }
                  public int size() {
                      return mCount;
                  }
                  public boolean contains(Object o) {
                      return containsKey(o);
                  }
                  public boolean remove(Object o) {
                      return o == null ? false : IdentityMap.this.remove(o) == o;
                  }
                  public void clear() {
                      IdentityMap.this.clear();
                  }
                  public String toString() {
                      return IdentityMap.this.toString(this);
                  }
              };
          }
          return mKeySet;
      }
      
      /**
       * Returns a collection view of the values contained in this map.  The
       * collection is backed by the map, so changes to the map are reflected in
       * the collection, and vice-versa.  The collection supports element
       * removal, which removes the corresponding mapping from this map, via the
       * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
       * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations.
       * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.
       *
       * @return a collection view of the values contained in this map.
       */
      public Collection values() {
          if (mValues==null) {
              mValues = new AbstractCollection() {
                  public Iterator iterator() {
                      return getHashIterator(VALUES);
                  }
                  public int size() {
                      return mCount;
                  }
                  public boolean contains(Object o) {
                      return containsValue(o);
                  }
                  public void clear() {
                      IdentityMap.this.clear();
                  }
                  public String toString() {
                      return IdentityMap.this.toString(this);
                  }
              };
          }
          return mValues;
      }
  
      /**
       * Returns a collection view of the mappings contained in this map.  Each
       * element in the returned collection is a <tt>Map.Entry</tt>.  The
       * collection is backed by the map, so changes to the map are reflected in
       * the collection, and vice-versa.  The collection supports element
       * removal, which removes the corresponding mapping from the map, via the
       * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
       * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations.
       * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.
       *
       * @return a collection view of the mappings contained in this map.
       * @see Map.Entry
       */
      public Set entrySet() {
          if (mEntrySet==null) {
              mEntrySet = new AbstractSet() {
                  public Iterator iterator() {
                      return getHashIterator(ENTRIES);
                  }
                  
                  public boolean contains(Object o) {
                      if (!(o instanceof Map.Entry)) {
                          return false;
                      }
                      Map.Entry entry = (Map.Entry)o;
                      Object key = entry.getKey();
  
                      Entry tab[] = mTable;
                      int hash = System.identityHashCode(key);
                      int index = (hash & 0x7FFFFFFF) % tab.length;
  
                      for (Entry e = tab[index], prev = null; e != null; e = e.mNext) {
                          Object entryKey = e.getKey();
                          
                          if (entryKey == null) {
                              // Clean up after a cleared Reference.
                              mModCount++;
                              if (prev != null) {
                                  prev.mNext = e.mNext;
                              }
                              else {
                                  tab[index] = e.mNext;
                              }
                              mCount--;
                          }
                          else if (e.mHash == hash && e.identityEquals(entry)) {
                              return true;
                          }
                          else {
                              prev = e;
                          }
                      }
  
                      return false;
                  }
  
                  public boolean remove(Object o) {
                      if (!(o instanceof Map.Entry)) {
                          return false;
                      }
                      Map.Entry entry = (Map.Entry)o;
                      Object key = entry.getKey();
                      Entry tab[] = mTable;
                      int hash = System.identityHashCode(key);
                      int index = (hash & 0x7FFFFFFF) % tab.length;
  
                      for (Entry e = tab[index], prev = null; e != null; e = e.mNext) {
                          Object entryKey = e.getKey();
                          
                          if (entryKey == null) {
                              // Clean up after a cleared Reference.
                              mModCount++;
                              if (prev != null) {
                                  prev.mNext = e.mNext;
                              }
                              else {
                                  tab[index] = e.mNext;
                              }
                              mCount--;
                          }
                          else if (e.mHash == hash && e.identityEquals(entry)) {
                              mModCount++;
                              if (prev != null) {
                                  prev.mNext = e.mNext;
                              }
                              else {
                                  tab[index] = e.mNext;
                              }
                              mCount--;
  
                              e.mValue = null;
                              return true;
                          }
                          else {
                              prev = e;
                          }
                      }
                      return false;
                  }
  
                  public int size() {
                      return mCount;
                  }
                  
                  public void clear() {
                      IdentityMap.this.clear();
                  }
  
                  public String toString() {
                      return IdentityMap.this.toString(this);
                  }
              };
          }
          
          return mEntrySet;
      }
      
      public String toString() {
          StringBuffer buf = new StringBuffer();
          Iterator it = entrySet().iterator();
          
          buf.append("{");
          for (int i = 0; it.hasNext(); i++) {
              if (i > 0) {
                  buf.append(", ");
              }
              Map.Entry e = (Map.Entry)it.next();
              buf.append(e.getKey() + "=" + e.getValue());
          }
          buf.append("}");
          return buf.toString();
      }
  
      private Iterator getHashIterator(int type) {
          if (mCount == 0) {
              return cEmptyHashIterator;
          }
          else {
              return new HashIterator(type);
          }
      }
  
      /**
       * HashMap collision list entry.
       */
      private static class Entry implements Map.Entry {
          int mHash;
          Object mValue;
          Entry mNext;
          
          private Reference mKey;
  
          Entry(int hash, Object key, Object value, Entry next) {
              mHash = hash;
              mKey = new WeakReference(key);
              mValue = value;
              mNext = next;
          }
          
          private Entry(int hash, Reference key, Object value, Entry next) {
              mHash = hash;
              mKey = key;
              mValue = value;
              mNext = next;
          }
          
          protected Object clone() {
              return new Entry(mHash, (Reference)mKey, mValue,
                               (mNext==null ? null : (Entry)mNext.clone()));
          }
          
          // Map.Entry Ops 
          
          public Object getKey() {
              return mKey.get();
          }
          
          public Object getValue() {
              return mValue;
          }
          
          public Object setValue(Object value) {
              Object oldValue = mValue;
              mValue = value;
              return oldValue;
          }
          
          public boolean equals(Object o) {
              if (!(o instanceof Map.Entry)) {
                  return false;
              }
              Map.Entry e = (Map.Entry)o;
  
              Object key = getKey();
              
              return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
                  (mValue==null ? e.getValue()==null : mValue.equals(e.getValue()));
          }
  
          public boolean identityEquals(Map.Entry e) {
              return (getKey() == e.getKey()) &&
                  (mValue==null ? e.getValue()==null : mValue.equals(e.getValue()));
          }
          
          public int hashCode() {
              return mHash ^ (mValue==null ? 0 : mValue.hashCode());
          }
          
          public String toString() {
              return getKey() + "=" + mValue;
          }
      }
  
      private class HashIterator implements Iterator {
          private Entry[] mTable = IdentityMap.this.mTable;
          private int mIndex = mTable.length;
          private Entry mEntry;
          // To ensure that the iterator doesn't return cleared entries, keep a
          // hard reference to the key. Its existence will prevent the weak
          // key from being cleared.
          private Object mEntryKey;
          private Entry mLastReturned;
          private int mType;
          
          /**
           * The modCount value that the iterator believes that the backing
           * List should have.  If this expectation is violated, the iterator
           * has detected concurrent modification.
           */
          private int expectedModCount = mModCount;
          
          HashIterator(int type) {
              mType = type;
          }
          
          public boolean hasNext() {
              while (mEntry == null ||
                     (mEntryKey = mEntry.getKey()) == null) {
                  if (mEntry != null) {
                      // Clean up after a cleared Reference.
                      remove(mEntry);
                      mEntry = mEntry.mNext;
                  }
  
                  if (mEntry == null) {
                      if (mIndex <= 0) {
                          return false;
                      }
                      else {
                          mEntry = mTable[--mIndex];
                      }
                  }
              }
  
              return true;
          }
          
          public Object next() {
              if (mModCount != expectedModCount) {
                  throw new ConcurrentModificationException();
              }
              
              if (!hasNext()) {
                  throw new NoSuchElementException();
              }
  
              mLastReturned = mEntry;
              mEntry = mEntry.mNext;
  
              return mType == KEYS ? mLastReturned.getKey() :
                  (mType == VALUES ? mLastReturned.getValue() : mLastReturned);
          }
          
          public void remove() {
              if (mLastReturned == null) {
                  throw new IllegalStateException();
              }
              if (mModCount != expectedModCount) {
                  throw new ConcurrentModificationException();
              }
              remove(mLastReturned);
              mLastReturned = null;
          }
  
          private void remove(Entry toRemove) {
              Entry[] tab = mTable;
              int index = (toRemove.mHash & 0x7FFFFFFF) % tab.length;
              
              for (Entry e = tab[index], prev = null; e != null; e = e.mNext) {
                  if (e == toRemove) {
                      mModCount++;
                      expectedModCount++;
                      if (prev == null) {
                          tab[index] = e.mNext;
                      }
                      else {
                          prev.mNext = e.mNext;
                      }
                      mCount--;
                      return;
                  }
                  else {
                      prev = e;
                  }
              }
              throw new ConcurrentModificationException();
          }
      }
  }
  
  
  
  1.1                  jakarta-velocity/src/java/org/apache/velocity/io/InternedCharToByteBuffer.java
  
  Index: InternedCharToByteBuffer.java
  ===================================================================
  package org.apache.velocity.io;
  
  /* ====================================================================
   * TeaServlet - Copyright (c) 1999-2000 Walt Disney Internet Group
   * ====================================================================
   * The Tea Software License, Version 1.1
   *
   * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Walt Disney Internet Group (http://opensource.go.com/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact opensource@dig.com.
   *
   * 5. Products derived from this software may not be called "Tea",
   *    "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
   *    "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
   *    written permission of the Walt Disney Internet Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS
   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   * ====================================================================
   *
   * For more information about Tea, please see http://opensource.go.com/.
   */
  
  import java.io.*;
  import java.util.*;
  
  /******************************************************************************
   * A CharToByteBuffer that keeps track of interned strings (mainly string
   * literals) and statically caches the results of those strings after applying
   * a byte conversion. This can improve performance if many of the strings being
   * passed to the append method have been converted before.
   *
   * @author Brian S O'Neill
   * @version
   * <!--$$Revision: 1.1 $-->, <!--$$JustDate:-->  9/07/00 <!-- $-->
   */
  public class InternedCharToByteBuffer implements CharToByteBuffer {
      private static final Object MARKER = new Object();
  
      // Maps String encodings to caches of Strings converted using that
      // encoding. Use a TreeMap because it is good for small maps.
      private static Map cEncodings = new TreeMap();
  
      private static Random cLastRandom = new Random();
  
      private static Map getConvertedCache(String encoding) {
          synchronized (cEncodings) {
              Map cache = (Map)cEncodings.get(encoding);
              if (cache == null) {
                  cache =
                      Collections.synchronizedMap(new IdentityMap());
                  cEncodings.put(encoding, cache);
              }
              return cache;
          }
      }
  
      private static Random getRandom() {
          synchronized (cLastRandom) {
              return cLastRandom = new Random(cLastRandom.nextLong());
          }
      }
  
      private CharToByteBuffer mBuffer;
      private Map mConvertedCache;
      private Random mRandom;
  
      public InternedCharToByteBuffer(CharToByteBuffer buffer)
          throws IOException
      {
          mBuffer = buffer;
          String encoding = buffer.getEncoding();
          if (encoding != null) {
              mConvertedCache = getConvertedCache(buffer.getEncoding());
          }
          mRandom = getRandom();
      }
  
      public void setEncoding(String enc) throws IOException {
          mBuffer.setEncoding(enc);
          mConvertedCache = getConvertedCache(mBuffer.getEncoding());
      }
  
      public String getEncoding() throws IOException {
          return mBuffer.getEncoding();
      }
  
      public long getBaseByteCount() throws IOException {
          return mBuffer.getBaseByteCount();
      }
  
      public long getByteCount() throws IOException {
          return mBuffer.getByteCount();
      }
      
      public void writeTo(OutputStream out) throws IOException {
          mBuffer.writeTo(out);
      }
      
      public void append(byte b) throws IOException {
          mBuffer.append(b);
      }
      
      public void append(byte[] bytes) throws IOException {
          mBuffer.append(bytes);
      }
      
      public void append(byte[] bytes, int offset, int length)
          throws IOException {
  
          mBuffer.append(bytes, offset, length);
      }
      
      public void appendSurrogate(ByteData s) throws IOException {
          mBuffer.appendSurrogate(s);
      }
  
      public void addCaptureBuffer(ByteBuffer buffer) throws IOException {
          mBuffer.addCaptureBuffer(buffer);
      }
      
      public void removeCaptureBuffer(ByteBuffer buffer) throws IOException {
          mBuffer.removeCaptureBuffer(buffer);
      }
      
      public void append(char c) throws IOException {
          mBuffer.append(c);
      }
      
      public void append(char[] chars) throws IOException {
          mBuffer.append(chars);
      }
      
      public void append(char[] chars, int offset, int length) 
          throws IOException {
  
          mBuffer.append(chars, offset, length);
      }
      
      public void append(String str) throws IOException {
          if (str.length() == 0) {
              return;
          }
  
          Map cache;
          if ((cache = mConvertedCache) == null) {
              mBuffer.append(str);
              return;
          }
  
          // Caching performed using a two pass technique. This is done to
          // avoid the cost of String.getBytes() for strings that aren't
          // actually interned.
  
          Object value;
  
          if ((value = cache.get(str)) != null) {
              ///*
              byte[] bytes;
              if (value != MARKER) {
                  bytes = (byte[])value;
              }
              else {
                  // This is at least the second time the string has been seen,
                  // so assume it has been interned and call String.getBytes().
                  bytes = str.getBytes(getEncoding());
                  cache.put(str, bytes);
              }
  
              mBuffer.append(bytes);
              //*/
  
              /*
              ByteData data;
              if (value != MARKER) {
                  data = (ByteData)value;
              }
              else {
                  // This is at least the second time the string has been seen,
                  // so assume it has been interned and call String.getBytes().
                  data = new ArrayByteData(str.getBytes(getEncoding()));
                  cache.put(str, data);
              }
  
              mBuffer.appendSurrogate(data);
              */
          }
          else {
              // Just put a marker at first to indicate that the string has been
              // seen, but don't call String.getBytes() just yet.
              if ((mRandom.nextInt() % 20) == 0) {
                  // Only mark sometimes in order to reduce the amount of times
                  // put is called for strings that will never be seen again.
                  // Calculating a random number is cheaper than putting into an
                  // IdentityMap because no objects are created. A consequence of
                  // this optimization is that it will take more iterations to
                  // discover the real string literals, but they will be
                  // discovered eventually.
                  cache.put(str, MARKER);
              }
              mBuffer.append(str);
          }
      }
      
      public void append(String str, int offset, int length) throws IOException {
          mBuffer.append(str, offset, length);
      }
  
      public void reset() throws IOException {
          mBuffer.reset();
      }
  }
  
  
  
  1.1                  jakarta-velocity/src/java/org/apache/velocity/io/SpilloverByteBuffer.java
  
  Index: SpilloverByteBuffer.java
  ===================================================================
  package org.apache.velocity.io;
  
  /* ====================================================================
   * TeaServlet - Copyright (c) 1999-2000 Walt Disney Internet Group
   * ====================================================================
   * The Tea Software License, Version 1.1
   *
   * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Walt Disney Internet Group (http://opensource.go.com/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact opensource@dig.com.
   *
   * 5. Products derived from this software may not be called "Tea",
   *    "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
   *    "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
   *    written permission of the Walt Disney Internet Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS
   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   * ====================================================================
   *
   * For more information about Tea, please see http://opensource.go.com/.
   */
  
  import java.io.OutputStream;
  import java.io.IOException;
  import java.util.List;
  import java.util.ArrayList;
  
  /******************************************************************************
   * A ByteBuffer implementation that initially stores its data in a
   * DefaultByteBuffer, but after a certain threshold is reached, spills over
   * into a FileByteBuffer.
   *
   * @author Brian S O'Neill
   * @version
   * <!--$$Revision: 1.1 $-->, <!--$$JustDate:-->  9/07/00 <!-- $-->
   */
  public class SpilloverByteBuffer implements ByteBuffer {
      private Group mGroup;
      
      private ByteBuffer mLocalBuffer;
      private ByteBuffer mSpillover;
  
      private List mCaptureBuffers;
  
      /**
       * Create a SpilloverByteBuffer against a Group that sets the threshold
       * and can create a spillover FileByteBuffer. The Group can be shared
       * among many SpilloverByteBuffers.
       *
       * @param group a group that can be shared among many SpilloverByteBuffers
       */
      public SpilloverByteBuffer(Group group) {
          mGroup = group;
          mLocalBuffer = new DefaultByteBuffer();
      }
  
      public long getBaseByteCount() throws IOException {
          if (mSpillover == null) {
              return mLocalBuffer.getBaseByteCount();
          }
          else {
              return mSpillover.getBaseByteCount();
          }
      }
  
      public long getByteCount() throws IOException {
          if (mSpillover == null) {
              return mLocalBuffer.getByteCount();
          }
          else {
              return mSpillover.getByteCount();
          }
      }
  
      public void writeTo(OutputStream out) throws IOException {
          if (mSpillover == null) {
              mLocalBuffer.writeTo(out);
          }
          else {
              mSpillover.writeTo(out);
          }
      }
  
      public void append(byte b) throws IOException {
          List captureBuffers;
          if ((captureBuffers = mCaptureBuffers) != null) {
              int size = captureBuffers.size();
              for (int i=0; i<size; i++) {
                  ((ByteBuffer)captureBuffers.get(i)).append(b);
              }
          }
  
          if (mSpillover == null) {
              if (mGroup.adjustLevel(1)) {
                  mLocalBuffer.append(b);
                  return;
              }
              spillover();
          }
  
          mSpillover.append(b);
      }
  
      public void append(byte[] bytes) throws IOException {
          append(bytes, 0, bytes.length);
      }
  
      public void append(byte[] bytes, int offset, int length)
          throws IOException
      {
          List captureBuffers;
          if ((captureBuffers = mCaptureBuffers) != null) {
              int size = captureBuffers.size();
              for (int i=0; i<size; i++) {
                  ((ByteBuffer)captureBuffers.get(i)).append
                      (bytes, offset, length);
              }
          }
  
          if (mSpillover == null) {
              if (mGroup.adjustLevel(length)) {
                  mLocalBuffer.append(bytes, offset, length);
                  return;
              }
              spillover();
          }
  
          mSpillover.append(bytes, offset, length);
      }
  
      public void appendSurrogate(ByteData s) throws IOException {
          if (s == null) {
              return;
          }
  
          List captureBuffers;
          if ((captureBuffers = mCaptureBuffers) != null) {
              int size = captureBuffers.size();
              for (int i=0; i<size; i++) {
                  ((ByteBuffer)captureBuffers.get(i)).appendSurrogate(s);
              }
          }
  
          if (mSpillover == null) {
              mLocalBuffer.appendSurrogate(s);
          }
          else {
              mSpillover.appendSurrogate(s);
          }
      }
  
      public void addCaptureBuffer(ByteBuffer buffer) {
          List captureBuffers;
          if ((captureBuffers = mCaptureBuffers) == null) {
              captureBuffers = mCaptureBuffers = new ArrayList();
          }
          captureBuffers.add(buffer);
      }
  
      public void removeCaptureBuffer(ByteBuffer buffer) {
          List captureBuffers;
          if ((captureBuffers = mCaptureBuffers) != null) {
              captureBuffers.remove(buffer);
          }
      }
  
      public void reset() throws IOException {
          mLocalBuffer.reset();
          if (mSpillover != null) {
              mSpillover.reset();
          }
          
          List captureBuffers;
          if ((captureBuffers = mCaptureBuffers) != null) {
              int size = captureBuffers.size();
              for (int i=0; i<size; i++) {
                  ((ByteData)captureBuffers.get(i)).reset();
              }
          }
      }
  
      protected void finalize() throws IOException {
          if (mLocalBuffer != null) {
              long count = mLocalBuffer.getBaseByteCount();
              mLocalBuffer = null;
              mGroup.adjustLevel(-count);
          }
      }
  
      private void spillover() throws IOException {
          mSpillover = mGroup.createFileByteBuffer();
          // TODO: This is bad! By writing out the contents of the existing
          // buffer early, surrogates are evaluated too soon!
          mLocalBuffer.writeTo(new ByteBufferOutputStream(mSpillover));
  
          long count = mLocalBuffer.getBaseByteCount();
          mLocalBuffer = null;
          mGroup.adjustLevel(-count);
      }
  
      public static abstract class Group {
          private final long mThreshold;
          private long mLevel;
  
          public Group(long threshold) {
              mThreshold = threshold;
          }
  
          public final long getThreshold() {
              return mThreshold;
          }
  
          public final synchronized long getCurrentLevel() {
              return mLevel;
          }
  
          public abstract FileByteBuffer createFileByteBuffer()
              throws IOException;
  
          synchronized boolean adjustLevel(long delta) {
              long newLevel;
              if ((newLevel = mLevel + delta) > mThreshold) {
                  return false;
              }
              else {
                  if (newLevel < 0) {
                      newLevel = 0;
                  }
                  mLevel = newLevel;
                  return true;
              }
          }
      }
  }
  
  
  
  1.1                  jakarta-velocity/src/java/org/apache/velocity/io/TemporaryFile.java
  
  Index: TemporaryFile.java
  ===================================================================
  package org.apache.velocity.io;
  
  /* ====================================================================
   * TeaServlet - Copyright (c) 1999-2000 Walt Disney Internet Group
   * ====================================================================
   * The Tea Software License, Version 1.1
   *
   * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Walt Disney Internet Group (http://opensource.go.com/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact opensource@dig.com.
   *
   * 5. Products derived from this software may not be called "Tea",
   *    "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
   *    "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
   *    written permission of the Walt Disney Internet Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS
   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
   * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   * ====================================================================
   *
   * For more information about Tea, please see http://opensource.go.com/.
   */
  
  import java.io.*;
  import java.util.*;
  
  /******************************************************************************
   * Provides references to temporary files that are automatically deleted when
   * closed, finalized, or when the system exits.
   *
   * @author Brian S O'Neill
   * @version
   * <!--$$Revision: 1.1 $-->, <!--$$JustDate:-->  9/07/00 <!-- $-->
   */
  public class TemporaryFile extends RandomAccessFile {
      /**
       * Creates a new writable temporary file that is deleted when closed,
       * finalized, or when the system exits.
       *
       * @see File#createTempFile
       */
      public static RandomAccessFile createTemporaryFile
          (String prefix, String suffix, File directory) throws IOException {
  
          File file = File.createTempFile(prefix, suffix, directory);
          file.deleteOnExit();
          return new TemporaryFile(file);
      }
  
      /**
       * Creates a new writable temporary file that is deleted when closed,
       * finalized, or when the system exits.
       *
       * @see File#createTempFile
       */
      public static RandomAccessFile createTemporaryFile
          (String prefix, String suffix) throws IOException {
  
          return createTemporaryFile(prefix, suffix, null);
      }
  
      private File mFile;
  
      private TemporaryFile(File file) throws IOException {
          super(file, "rw");
          mFile = file;
      }
  
      public void close() throws IOException {
          try {
              super.close();
          }
          finally {
              mFile.delete();
          }
      }
  
      protected void finalize() throws IOException {
          close();
      }
  }
  
  
  
  1.5       +1 -2      jakarta-velocity/src/java/org/apache/velocity/runtime/defaults/velocity.properties
  
  Index: velocity.properties
  ===================================================================
  RCS file: /home/cvs/jakarta-velocity/src/java/org/apache/velocity/runtime/defaults/velocity.properties,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- velocity.properties	2000/10/31 05:07:43	1.4
  +++ velocity.properties	2000/11/03 23:26:48	1.5
  @@ -8,8 +8,7 @@
   template.modificationCheckInterval = 2
   template.path=.
   template.cache=false
  -template.encoding = UTF8
  -template.asciihack = false
  +template.encoding=8859_1
   counter.name = velocityCount
   counter.initial.value = 1
   default.contentType=text/html
  
  
  
  1.11      +22 -18    jakarta-velocity/src/java/org/apache/velocity/servlet/VelocityServlet.java
  
  Index: VelocityServlet.java
  ===================================================================
  RCS file: /home/cvs/jakarta-velocity/src/java/org/apache/velocity/servlet/VelocityServlet.java,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- VelocityServlet.java	2000/10/27 23:12:42	1.10
  +++ VelocityServlet.java	2000/11/03 23:26:50	1.11
  @@ -70,7 +70,7 @@
   
   import org.apache.velocity.runtime.Runtime;
   
  -import org.apache.velocity.io.FastWriter;
  +import org.apache.velocity.io.*;
   
   /**
    * Base class which simplifies the use of Velocity with Servlets.
  @@ -93,7 +93,7 @@
    *
    * @author Dave Bryson
    * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
  - * $Id: VelocityServlet.java,v 1.10 2000/10/27 23:12:42 daveb Exp $
  + * $Id: VelocityServlet.java,v 1.11 2000/11/03 23:26:50 jon Exp $
    */
   public abstract class VelocityServlet extends HttpServlet
   {
  @@ -118,12 +118,6 @@
       private static String encoding = null;
   
       /**
  -     * Whether to use the <code>FasterWriter</code> hack for faster generation 
  -     * of ASCII output.
  -     */
  -    private static boolean asciiHack = true;
  -
  -    /**
        * The default content type.
        */
       private static String defaultContentType;
  @@ -172,8 +166,7 @@
               defaultContentType = 
                   Runtime.getString(Runtime.DEFAULT_CONTENT_TYPE, "text/html");
               
  -            encoding = Runtime.getString(Runtime.TEMPLATE_ENCODING);
  -            asciiHack = Runtime.getBoolean(Runtime.TEMPLATE_ASCIIHACK);
  +            encoding = Runtime.getString(Runtime.TEMPLATE_ENCODING, "8859_1");
           }
           catch( Exception e )
           {
  @@ -209,7 +202,7 @@
            throws ServletException, IOException
       {
           ServletOutputStream output = response.getOutputStream();
  -        FastWriter writer = null;
  +        CharToByteBufferWriter buffer = null;
           String contentType = null;
           try
           {
  @@ -237,10 +230,22 @@
               if ( template == null )
                   throw new Exception ("Cannot find the template!" );
               
  -            // write the data out.
  -            writer = new FastWriter(output, encoding);
  -            writer.setAsciiHack(asciiHack);
  -            template.merge( context, writer );
  +            // create the output buffer
  +            InternedCharToByteBuffer ictbb = new InternedCharToByteBuffer(
  +                new DefaultCharToByteBuffer(new DefaultByteBuffer(), encoding));
  +            buffer = new CharToByteBufferWriter (ictbb);
  +
  +            // merge the context with the template and output in the buffer
  +            template.merge( context, buffer );
  +
  +            // set the content length
  +            long length = ictbb.getByteCount();
  +            if (length <= Integer.MAX_VALUE)
  +            {
  +                response.setContentLength((int)length);
  +            }
  +
  +            ictbb.writeTo(output);
           }
           catch (Exception e)
           {
  @@ -252,10 +257,9 @@
               try
               {
                   // flush and close
  -                if (writer != null)
  +                if (buffer != null)
                   {
  -                    writer.flush();            
  -                    writer.close();                
  +                    buffer.close();
                   }
                   if (output != null)
                   {
  
  
  
  1.15      +25 -48    jakarta-velocity/src/java/org/apache/velocity/test/TemplateTestCase.java
  
  Index: TemplateTestCase.java
  ===================================================================
  RCS file: /home/cvs/jakarta-velocity/src/java/org/apache/velocity/test/TemplateTestCase.java,v
  retrieving revision 1.14
  retrieving revision 1.15
  diff -u -r1.14 -r1.15
  --- TemplateTestCase.java	2000/10/31 04:44:11	1.14
  +++ TemplateTestCase.java	2000/11/03 23:26:51	1.15
  @@ -64,7 +64,7 @@
   import org.apache.velocity.Template;
   import org.apache.velocity.test.provider.TestProvider;
   import org.apache.velocity.runtime.Runtime;
  -import org.apache.velocity.io.FastWriter;
  +import org.apache.velocity.io.*;
   import org.apache.velocity.util.StringUtils;
   
   
  @@ -73,7 +73,7 @@
    *
    * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
    * @author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
  - * @version $Id: TemplateTestCase.java,v 1.14 2000/10/31 04:44:11 geirm Exp $
  + * @version $Id: TemplateTestCase.java,v 1.15 2000/11/03 23:26:51 jon Exp $
    */
   public class TemplateTestCase extends RuntimeTestCase
   {
  @@ -108,11 +108,6 @@
        */
       protected String baseFileName;
   
  -    /**
  -     * The writer used to output evaluated templates.
  -     */
  -    private FastWriter writer;
  -
       private TestProvider provider;
       private ArrayList al;
       private Hashtable h;
  @@ -162,10 +157,30 @@
               Template template = Runtime.getTemplate
                   (getFileName(null, baseFileName, TMPL_FILE_EXT));
               assureResultsDirectoryExists();
  -            template.merge(context, getWriter(new FileOutputStream
  -                (getFileName(RESULT_DIR, baseFileName, RESULT_FILE_EXT))));
  -            closeWriter();
  +
  +            // get the file to write to
  +            FileOutputStream fos = 
  +                new FileOutputStream (getFileName(RESULT_DIR, baseFileName, RESULT_FILE_EXT));
  +
  +            // create the streams
  +            InternedCharToByteBuffer ictbb = new InternedCharToByteBuffer(
  +                new DefaultCharToByteBuffer(new DefaultByteBuffer(), Runtime.getString(
  +                    Runtime.TEMPLATE_ENCODING)));
  +            // create the writer
  +            CharToByteBufferWriter buffer = new CharToByteBufferWriter (ictbb);
  +
  +            // process the template
  +            template.merge(context, buffer);
  +
  +            // write the output to the file
  +            ictbb.writeTo(fos);
               
  +            // close the buffer
  +            buffer.close();
  +
  +            // close the file
  +            fos.close();
  +            
               if (!isMatch())
               {
                   fail("Processed template did not match expected output");
  @@ -255,43 +270,5 @@
       protected void tearDown () throws Exception
       {
           // No op.
  -    }
  -
  -    /**
  -     * Returns a <code>FastWriter</code> instance.
  -     *
  -     * @param out The output stream for the writer to write to.  If 
  -     *            <code>null</code>, defaults to <code>System.out</code>.
  -     * @return    The writer.
  -     */
  -    protected Writer getWriter (OutputStream out)
  -        throws UnsupportedEncodingException, IOException
  -    {
  -        if (writer == null)
  -        {
  -            if (out == null)
  -            {
  -                out = System.out;
  -            }
  -
  -            writer = new FastWriter
  -                (out, Runtime.getString(Runtime.TEMPLATE_ENCODING));
  -            writer.setAsciiHack
  -                (Runtime.getBoolean(Runtime.TEMPLATE_ASCIIHACK));
  -        }
  -        return writer;
  -    }
  -
  -    /**
  -     * Closes the writer (if it has been opened).
  -     */
  -    protected void closeWriter ()
  -        throws IOException
  -    {
  -        if (writer != null)
  -        {
  -            writer.flush();
  -            writer.close();
  -        }
       }
   }
  
  
  
  1.2       +22 -18    jakarta-velocity/src/java/org/apache/velocity/test/misc/Test.java
  
  Index: Test.java
  ===================================================================
  RCS file: /home/cvs/jakarta-velocity/src/java/org/apache/velocity/test/misc/Test.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Test.java	2000/10/25 00:15:30	1.1
  +++ Test.java	2000/11/03 23:26:52	1.2
  @@ -54,17 +54,15 @@
    * <http://www.apache.org/>.
    */
   
  -import java.io.FileWriter;
  -import java.io.FileOutputStream;
  +import java.io.*;
   
   import java.util.ArrayList;
   import java.util.Hashtable;
   
  -import org.apache.velocity.io.FastWriter;
  -
   import org.apache.velocity.Context;
   import org.apache.velocity.Template;
   
  +import org.apache.velocity.io.*;
   import org.apache.velocity.runtime.Runtime;
   import org.apache.velocity.test.provider.TestProvider;
   
  @@ -73,7 +71,7 @@
    * test all the directives support by Velocity.
    *
    * @author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
  - * @version $Id: Test.java,v 1.1 2000/10/25 00:15:30 jvanzyl Exp $
  + * @version $Id: Test.java,v 1.2 2000/11/03 23:26:52 jon Exp $
    */
   public class Test
   {
  @@ -88,8 +86,10 @@
           try
           {
               Runtime.init("velocity.properties");
  +            if (templateFile == null)
  +                templateFile = "examples/example.vm";
               Template template = Runtime.getTemplate(templateFile);
  -            
  +
               Context context = new Context();
               context.put("provider", provider);
               context.put("name", "jason");
  @@ -101,17 +101,17 @@
               context.put("searchResults", provider.getRelSearches());
               context.put("menu", provider.getMenu());
               context.put("stringarray", provider.getArray());
  -            
  -            FastWriter fw = new FastWriter(
  -                System.out, Runtime.getString(
  -                    Runtime.TEMPLATE_ENCODING));
  -
  -            fw.setAsciiHack(Runtime.getBoolean(
  -                Runtime.TEMPLATE_ASCIIHACK));
  -
  -            template.merge(context, fw);
  -            fw.flush();
  -            fw.close();
  +
  +            // create the output buffer
  +            InternedCharToByteBuffer ictbb = new InternedCharToByteBuffer(
  +                new DefaultCharToByteBuffer(new DefaultByteBuffer(), Runtime.getString(
  +                    Runtime.TEMPLATE_ENCODING)));
  +            CharToByteBufferWriter buffer = new CharToByteBufferWriter (ictbb);
  +            template.merge(context, buffer);
  +            // write the buffer to the output stream
  +            ictbb.writeTo(System.out);
  +            ictbb.reset(); // calling this doesn't really have an effect
  +            buffer.close();
           }
           catch( Exception e )
           {
  @@ -121,6 +121,10 @@
   
       public static void main(String[] args)
       {
  -        Test t = new Test(args[0]);
  +        Test t;
  +        if (args.length > 0)
  +            t = new Test(args[0]);
  +        else
  +            t = new Test(null);
       }
   }