/*
 * Copyright 2004-2005 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.codehaus.groovy.grails.web.pages;

import javax.servlet.ServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.*;

/**
 * NOTE: Based on work done by on the GSP standalone project (https://gsp.dev.java.net/)
 *
 * A buffered writer that won't commit the response until the buffer has reached the high
 * water mark, or until flush() or close() is called.
 *
 * @author Troy Heninger
 * @author Graeme Rocher
 *
 * Date: Jan 10, 2004
 *
 */
public class GSPResonseWriter extends PrintWriter {
	private static final Log LOG = LogFactory.getLog(GSPResonseWriter.class);
	private ServletResponse response;
	private CharArrayWriter out0 = new CharArrayWriter();
	private Writer out1;
	private int max;
	private boolean trouble = false;

	/**
	 * Private constructor.  Use getInstance() instead.
	 * @param response
	 * @param out
	 * @param max
	 */
	private GSPResonseWriter(ServletResponse response, CharArrayWriter out, int max) {
		super(out);
		this.response = response;
		this.out0 = out;
		this.max = max;
	} // GSPResonseWriter

	/**
	 * Private constructor.  Use getInstance() instead.
	 * @param writer The target writer to write to
	 * @param out
	 * @param max
	 */
	private GSPResonseWriter(Writer writer, CharArrayWriter out, int max) {
		super(out);
		this.out0 = out;
        this.out1 = writer;
        this.max = max;
	}

	/**
	 * Flush the stream if it's not closed and check its error state.
	 * Errors are cumulative; once the stream encounters an error, this
	 * routine will return true on all successive calls.
	 *
	 * @return True if the print stream has encountered an error, either on the
	 * underlying output stream or during a format conversion.
	 */
	public boolean checkError() {
		if (super.checkError()) return true;
		return trouble;
	} // checkError()

	/**
	 * Close the stream.
	 * @see #checkError()
	 */
	public void close() {
		if (!response.isCommitted()) {
			response.setContentLength(out0.size());
		}
		flush();
		super.close();
	} // close()

	/**
	 * Flush the stream.
	 * @see #checkError()
	 */
	public synchronized void flush() {
		if (trouble) return;
		super.flush();
		if (out1 == null) {
			try {
				out1 = response.getWriter();
			} catch (IOException e) {
				LOG.debug("I/O excepton flushing output in GSP response writer: " + e.getMessage(),e  );
				trouble = true;
				return;
			}
		}
		try {
			out1.write(out0.toCharArray());
			out0.reset();
		} catch (IOException e) {
			LOG.debug("I/O excepton flushing output in GSP response writer: " + e.getMessage(),e  );
			trouble = true;
		}
	} // flush()

	/**
	 * Static factory method to create the writer.
	 * @param response
	 * @param max
	 * @return  A GSPResonseWriter instance
	 */
	static GSPResonseWriter getInstance(ServletResponse response, int max) {
		return new GSPResonseWriter(response, new CharArrayWriter(max), max);
	} // getInstance()

	/**
	 * Static factory method to create the writer.
	 * @param target The target writer to write too
	 * @param max
	 * @return  A GSPResonseWriter instance
	 */
	static GSPResonseWriter getInstance(Writer target, int max) {
		return new GSPResonseWriter(target, new CharArrayWriter(max), max);
	} // getInstance()

    /**
	 * Print an object.  The string produced by the <code>{@link
	 * java.lang.String#valueOf(Object)}</code> method is translated into bytes
	 * according to the platform's default character encoding, and these bytes
	 * are written in exactly the manner of the <code>{@link #write(int)}</code>
	 * method.
	 *
	 * @param      obj   The <code>Object</code> to be printed
	 * @see        java.lang.Object#toString()
	 */
	public void print(Object obj) {
		if (obj == null) obj = "";
		String out = String.valueOf(obj);
		if(out == null)out = "";
		write(out);
	}

	/**
	 * Print a string.  If the argument is <code>null</code> then the string
	 * <code>""</code> is printed.  Otherwise, the string's characters are
	 * converted into bytes according to the platform's default character
	 * encoding, and these bytes are written in exactly the manner of the
	 * <code>{@link #write(int)}</code> method.
	 *
	 * @param      s   The <code>String</code> to be printed
	 */
	public void print(String s) {
		if (s == null) s = "";
		write(s);
	} // print()

	/**
	 * Write a single character.
	 * @param c int specifying a character to be written.
	 */
	public void write(int c) {
		if (trouble) return;
		super.write(c);
		if (out0.size() >= max) {
			flush();
		}
	} // write()

	/**
	 * Write a portion of an array of characters.
	 * @param buf Array of characters
	 * @param off Offset from which to start writing characters
	 * @param len Number of characters to write
	 */
	public void write(char buf[], int off, int len) {
		if (trouble || buf == null || len == 0) return;
		super.write(buf, off, len);
		if (out0.size() >= max) {
			flush();
		}
	} // write()

	/**
	 * Write a portion of a string.
	 * @param s A String
	 * @param off Offset from which to start writing characters
	 * @param len Number of characters to write
	 */
	public void write(String s, int off, int len) {
		if (trouble || s == null || s.length() == 0) return;
		super.write(s, off, len);
		if (out0.size() >= max) {
			flush();
		}
	} // write()

} // GSPResonseWriter

@@ -2585,16 +2585,26 @@
 ed()) %7B%0A
+%09%09%09try %7B%0A%09
 %09%09%09respo
@@ -2633,16 +2633,203 @@
 ut0.
-size());
+toString().getBytes(response.getCharacterEncoding()).length);%0A%09%09%09%7D catch (UnsupportedEncodingException e) %7B%0A%09%09%09%09LOG.error(%22Encoding error setting content length: %22 + e.getMessage(),e  );%09%09%09%09%0A%09%09%09%7D
 %0A%09%09%7D

patch to correct content length problem in GSP response writer. fixes GRAILS-303,GRAILS-223 and GRAILS-240