/FileExtraction/Java_unseen/dlitz_resin/modules/resin/src/com/caucho/jsp/BodyContentImpl.java
/*
 * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.jsp;

import com.caucho.util.CharBuffer;
import com.caucho.util.FreeList;
import com.caucho.util.L10N;
import com.caucho.vfs.TempCharBuffer;
import com.caucho.vfs.TempCharReader;
import com.caucho.vfs.TempCharStream;

import javax.servlet.jsp.JspWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.logging.Logger;

/**
 * Implementation of the JSP BodyContent interface.
 */
public class BodyContentImpl extends AbstractBodyContent {
  static final L10N L = new L10N(BodyContentImpl.class);
  private static final Logger log
    = Logger.getLogger(BodyContentImpl.class.getName());
  
  private static final FreeList<BodyContentImpl> _freeList
    = new FreeList<BodyContentImpl>(32);

  private TempCharStream _tempStream = new TempCharStream();
  private TempCharReader _charReader;
  private JspPrintWriter _printWriter;
  private JspWriter _prev;

  /**
   * Creates a new QBodyContent instance.
   *
   * @param prev the encloding writer.
   */
  private BodyContentImpl(JspWriter prev)
  {
    setParent(prev);
  }

  /**
   * Allocates a new BodyContent instance.
   */
  static BodyContentImpl allocate()
  {
    BodyContentImpl body = (BodyContentImpl) _freeList.allocate();
    
    if (body == null)
      body = new BodyContentImpl(null);

    return body;
  }

  /**
   * Initializes the BodyContent object.
   *
   * @param prev the enclosing writer
   */
  void init(JspWriter prev)
  {
    setParent(prev);

    _tempStream.openWrite();
  }

  /**
   * Writes characters to the stream.
   *
   * @param buf character buffer
   * @param off starting offset into the buffer
   * @param len length of valid bytes in the buffer.
   */
  final public void write(char []buf, int off, int len) throws IOException
  {
    _tempStream.write(buf, off, len);
  }

  /**
   * Writes characters to the stream.
   *
   * @param s string
   * @param off starting offset into the buffer
   * @param len length of valid bytes in the buffer.
   */
  final public void write(String s, int off, int len) throws IOException
  {
    _tempStream.write(s, off, len);
  }

  /**
   * Writes characters to the stream.
   *
   * @param ch character to write.
   */
  final public void write(int ch) throws IOException
  {
    _tempStream.write(ch);
  }

  final public void clear() throws IOException
  {
    _tempStream.clearWrite();
  }

  final public void clearBuffer() throws IOException
  {
    clear();
  }

  final public void flush() throws IOException
  {
    // jsp/18kg
    throw new IOException(L.l("flush() may not be called in a body"));
  }

  final public void close() throws IOException
  {
  }

  final public int getBufferSize()
  {
    return -1;
  }

  final public int getRemaining()
  {
    return 0;
  }

  /**
   * Clears the body contents.
   */
  public void clearBody()
  {
    _tempStream.clearWrite();
  }

  /**
   * Returns a reader to the body content.
   */
  public Reader getReader()
  {
    _charReader = new TempCharReader();
    _charReader.init(_tempStream.getHead());
    
    return _charReader;
  }

  public CharBuffer getCharBuffer()
  {
    CharBuffer cb = new CharBuffer();
      
    TempCharBuffer head = _tempStream.getHead();

    for (; head != null; head = head.getNext()) {
      char []cbuf = head.getBuffer();

      cb.append(cbuf, 0, head.getLength());
    }
      
    return cb;
  }

  /**
   * Returns a string representing the body content.
   */
  public String getString()
  {
    TempCharBuffer head = _tempStream.getHead();

    if (head == null || head.getBuffer().length == 0)
      return "";

    int bomLength = 0;

    if (head.getBuffer()[0] == 0xfeff)
      bomLength = 1;

    if (head.getNext() == null)
      return new String(head.getBuffer(), bomLength, head.getLength() - bomLength);

    int length = 0;
    for (; head != null; head = head.getNext())
      length += head.getLength();

    char []buf = new char[length];

    int offset = 0;
    for (head = _tempStream.getHead(); head != null; head = head.getNext()) {
      char []cbuf = head.getBuffer();
      int sublen = head.getLength();

      System.arraycopy(cbuf, 0, buf, offset, sublen);

      offset += sublen;
    }

    return new String(buf, bomLength, length - bomLength);
  }

  /**
   * Returns a string representing the body content.
   */
  public String getTrimString()
  {
    TempCharBuffer head = _tempStream.getHead();

    boolean hasData = false;

    char []buf = null;
    int totalLength = 0;
    for (; head != null; head = head.getNext()) {
      char []cbuf = head.getBuffer();
      int end = head.getLength();
      int offset = 0;

      if (! hasData) {
        for (offset = 0; offset < end; offset++) {
          if (! Character.isWhitespace(cbuf[offset])) {
            hasData = true;
            break;
          }
        }
      }

      if (head.getNext() == null) {
        for (; offset < end; end--) {
          if (! Character.isWhitespace(cbuf[end - 1]))
            break;
        }

        if (buf != null) {
          System.arraycopy(cbuf, offset, buf, totalLength, end - offset);
          totalLength += end - offset;

          return new String(buf, 0, totalLength);
        }
        else if (offset == end)
          return "";
        else
          return new String(cbuf, offset, end - offset);

      }
      else if (buf == null) {
        int length = 0;

        for (TempCharBuffer ptr = head; ptr != null; ptr = ptr.getNext())
          length += ptr.getLength();

        buf = new char[length];

        System.arraycopy(cbuf, offset, buf, 0, end - offset);
        totalLength += end - offset;
      }
      else {
        System.arraycopy(cbuf, offset, buf, totalLength, end - offset);
        totalLength += end - offset;
      }
    }

    return "";
  }

  /**
   * Writes the body contents out to the named writer.
   */
  public void writeOut(Writer out) throws IOException
  {
    try {
      TempCharBuffer head = _tempStream.getHead();
      boolean isFirst = true;

      for (; head != null; head = head.getNext()) {
        int offset = 0;
        int length = head.getLength();
        char []cbuf = head.getBuffer();

        if (isFirst && length > 0 && cbuf[0] == 0xfeff) {
          // skip byte-order-mark
          offset = 1;
          length--;
        }

        out.write(cbuf, offset, length);

        isFirst = false;
      }
    } catch (IOException e) {
    }
  }

  /**
   * Writes the body contents out to the named writer.
   */
  public void flushBuffer() throws IOException
  {
  }

  /**
   * Returns the print writer.
   */
  public PrintWriter getWriter()
  {
    if (_printWriter == null)
      _printWriter = new JspPrintWriter(this);

    _printWriter.init(this);

    return _printWriter;
  }
  
  /**
   * Releases the body content at the end of the tag.
   */
  public void release()
  {
    releaseNoFree();

    _freeList.free(this);
  }
  
  void releaseNoFree()
  {
    if (_charReader != null && ! _charReader.isEmpty()) {
      _charReader.setFree(true);
      _tempStream.discard();
    }
    else
      _tempStream.destroy();

    _charReader = null;
    _prev = null;
  }

  AbstractJspWriter popWriter()
  {
    AbstractJspWriter parent = super.popWriter();
    
    release();

    return parent;
  }
}
