/*
Copyright (C) 2000-2010  Ministere de la culture et de la communication (France), AJLSM
See LICENCE file
 */
package fr.gouv.culture.util.apache.cocoon.xml;

import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;

import fr.gouv.culture.sdx.utils.Utilities;

/**This class attempts to ensure synchronization with it's xml consumer
 * before sending any events the consumer resource will be released in
 * the endDocument() method
 *
 */
public class AbstractSynchronizedXMLPipe extends AbstractSynchronizedXMLProducer implements SynchronizedXMLPipe {

	protected synchronized void acquireSynchronizedXMLConsumer() throws SAXException {
		if ( this.synchronizedXmlConsumer != null
				&& !this.synchronizedXmlConsumer.acquired() )
		{
			try {
				this.synchronizedXmlConsumer.acquire();
			} catch (InterruptedException e) {
				throw new SAXException(e.getMessage(), e);
			}
		}
	}

	protected synchronized void releaseSynchronizedXMLConsumer() {
		if ( this.synchronizedXmlConsumer != null
				&& this.synchronizedXmlConsumer.acquired() )
		{
			this.synchronizedXmlConsumer.release();
		}
	}

	/**
	 * Receive an object for locating the origin of SAX document events.
	 *
	 * @param locator An object that can return the location of any SAX
	 *                document event.
	 */
	public void setDocumentLocator(Locator locator) {
		try {

			if (this.isAcquired()) {
				this.acquireSynchronizedXMLConsumer();
				if ( super.synchronizedXmlConsumer != null ){
					super.synchronizedXmlConsumer.setDocumentLocator(locator);
				}
			}
		} catch (SAXException e) {
			this.releaseSynchronizedXMLConsumer();
			e.printStackTrace();//TODO ?: what else can be done here?
		}
	}

	/**
	 * Acquire the synchronization of the consumer and
	 * receive notification of the beginning of a document.
	 */
	public void startDocument()
	throws SAXException {
		try {
			if (this.isAcquired())
			{
				this.acquireSynchronizedXMLConsumer();
				if (super.synchronizedXmlConsumer != null){
					super.synchronizedXmlConsumer.startDocument();
				}
			}
		} catch (SAXException e) {
			this.releaseSynchronizedXMLConsumer();
			throw e;
		}

	}

	/**
	 * Release the synchronized consumer and receive notification of the end of a document.
	 */
	public void endDocument()
	throws SAXException {
		try {
			this.acquireSynchronizedXMLConsumer();
			if ( this.isAcquired()
					&& super.synchronizedXmlConsumer != null )
			{
				super.synchronizedXmlConsumer.endDocument();
			}
		} finally {
			this.releaseSynchronizedXMLConsumer();
		}
	}

	/**
	 * Begin the scope of a prefix-URI Namespace mapping.
	 *
	 * @param prefix The Namespace prefix being declared.
	 * @param uri The Namespace URI the prefix is mapped to.
	 */
	public void startPrefixMapping(String prefix, String uri)
	throws SAXException {
		try {
			if (this.isAcquired()) {
				if (super.synchronizedXmlConsumer != null) super.synchronizedXmlConsumer.startPrefixMapping(prefix, uri);
			}
		} catch (SAXException e) {
			this.releaseSynchronizedXMLConsumer();
			throw e;
		}
	}

	/**
	 * End the scope of a prefix-URI mapping.
	 *
	 * @param prefix The prefix that was being mapping.
	 */
	public void endPrefixMapping(String prefix)
	throws SAXException {
		if ( Utilities.checkString(prefix)
				&& super.synchronizedXmlConsumer != null )
		{
			try {
				this.acquireSynchronizedXMLConsumer();
				if( this.isAcquired() ){
					super.synchronizedXmlConsumer.endPrefixMapping(prefix);
				}
			} catch (SAXException e) {
				this.releaseSynchronizedXMLConsumer();
				throw e;
			}
		}
	}

	/**
	 * Receive notification of the beginning of an element.
	 *
	 * @param uri The Namespace URI, or the empty string if the element has no
	 *            Namespace URI or if Namespace
	 *            processing is not being performed.
	 * @param loc The local name (without prefix), or the empty string if
	 *            Namespace processing is not being performed.
	 * @param raw The raw XML 1.0 name (with prefix), or the empty string if
	 *            raw names are not available.
	 * @param a The attributes attached to the element. If there are no
	 *          attributes, it shall be an empty Attributes object.
	 */
	public void startElement(String uri, String loc, String raw, Attributes a)
	throws SAXException {
		try {
			if ( this.isAcquired()
					&& super.synchronizedXmlConsumer != null )
			{
				super.synchronizedXmlConsumer.startElement(uri, loc, raw, a);
			}
		} catch (SAXException e) {
			this.releaseSynchronizedXMLConsumer();
			throw e;
		}
	}


	/**
	 * Receive notification of the end of an element.
	 *
	 * @param uri The Namespace URI, or the empty string if the element has no
	 *            Namespace URI or if Namespace
	 *            processing is not being performed.
	 * @param loc The local name (without prefix), or the empty string if
	 *            Namespace processing is not being performed.
	 * @param raw The raw XML 1.0 name (with prefix), or the empty string if
	 *            raw names are not available.
	 */
	public void endElement(String uri, String loc, String raw)
	throws SAXException {
		try {
			this.acquireSynchronizedXMLConsumer();
			if ( this.isAcquired()
					&& super.synchronizedXmlConsumer != null )
			{
				super.synchronizedXmlConsumer.endElement(uri, loc, raw);
			}
		} catch (SAXException e) {
			this.releaseSynchronizedXMLConsumer();
			throw e;
		}
	}

	/**
	 * Receive notification of character data.
	 *
	 * @param c The characters from the XML document.
	 * @param start The start position in the array.
	 * @param len The number of characters to read from the array.
	 */
	public void characters(char c[], int start, int len)
	throws SAXException {
		try {
			if ( this.isAcquired()
					&& super.synchronizedXmlConsumer != null )
			{
				super.synchronizedXmlConsumer.characters(c, start, len);
			}
		} catch (SAXException e) {
			this.releaseSynchronizedXMLConsumer();
			throw e;
		}
	}

	/**
	 * Receive notification of ignorable whitespace in element content.
	 *
	 * @param c The characters from the XML document.
	 * @param start The start position in the array.
	 * @param len The number of characters to read from the array.
	 */
	public void ignorableWhitespace(char c[], int start, int len)
	throws SAXException {
		try {
			if ( this.isAcquired()
					&& super.synchronizedXmlConsumer != null )
			{
				super.synchronizedXmlConsumer.ignorableWhitespace(c, start, len);
			}
		} catch (SAXException e) {
			this.releaseSynchronizedXMLConsumer();
			throw e;
		}
	}

	/**
	 * Receive notification of a processing instruction.
	 *
	 * @param target The processing instruction target.
	 * @param data The processing instruction data, or null if none was
	 *             supplied.
	 */
	public void processingInstruction(String target, String data)
	throws SAXException {
		try {
			if ( this.isAcquired()
					&& super.synchronizedXmlConsumer != null )
			{
				super.synchronizedXmlConsumer.processingInstruction(target, data);
			}
		} catch (SAXException e) {
			this.releaseSynchronizedXMLConsumer();
			throw e;
		}
	}

	/**
	 * Receive notification of a skipped entity.
	 *
	 * @param name The name of the skipped entity.  If it is a  parameter
	 *             entity, the name will begin with '%'.
	 */
	public void skippedEntity(String name)
	throws SAXException {
		try {
			if ( this.isAcquired()
					&& super.synchronizedXmlConsumer != null )
			{
				super.synchronizedXmlConsumer.skippedEntity(name);
			}
		} catch (SAXException e) {
			this.releaseSynchronizedXMLConsumer();
			throw e;
		}
	}

	/**
	 * Report the start of DTD declarations, if any.
	 *
	 * @param name The document type name.
	 * @param publicId The declared public _identifier for the external DTD
	 *                 subset, or null if none was declared.
	 * @param systemId The declared system _identifier for the external DTD
	 *                 subset, or null if none was declared.
	 */
	public void startDTD(String name, String publicId, String systemId)
	throws SAXException {
		try {
			if ( this.isAcquired()
					&& super.synchronizedXmlConsumer != null )
			{
				super.synchronizedXmlConsumer.startDTD(name, publicId, systemId);
			}
		} catch (SAXException e) {
			this.releaseSynchronizedXMLConsumer();
			throw e;
		}
	}

	/**
	 * Report the end of DTD declarations.
	 */
	public void endDTD()
	throws SAXException {
		try {
			this.acquireSynchronizedXMLConsumer();
			if ( this.isAcquired()
					&& super.synchronizedXmlConsumer != null )
			{
				super.synchronizedXmlConsumer.endDTD();
			}
		} catch (SAXException e) {
			this.releaseSynchronizedXMLConsumer();
			throw e;
		}
	}

	/**
	 * Report the beginning of an entity.
	 *
	 * @param name The name of the entity. If it is a parameter entity, the
	 *             name will begin with '%'.
	 */
	public void startEntity(String name)
	throws SAXException {
		try {
			if ( this.isAcquired()
					&& super.synchronizedXmlConsumer != null )
			{
				super.synchronizedXmlConsumer.startEntity(name);
			}
		} catch (SAXException e) {
			this.releaseSynchronizedXMLConsumer();
			throw e;
		}
	}

	/**
	 * Report the end of an entity.
	 *
	 * @param name The name of the entity that is ending.
	 */
	public void endEntity(String name)
	throws SAXException {
		try {
			this.acquireSynchronizedXMLConsumer();
			if ( this.isAcquired()
					&& super.synchronizedXmlConsumer != null )
			{
				super.synchronizedXmlConsumer.endEntity(name);
			}
		} catch (SAXException e) {
			this.releaseSynchronizedXMLConsumer();
			throw e;
		}
	}

	/**
	 * Report the start of a CDATA section.
	 */
	public void startCDATA()
	throws SAXException {
		try {
			if ( this.isAcquired()
					&& super.synchronizedXmlConsumer != null )
			{
				super.synchronizedXmlConsumer.startCDATA();
			}
		} catch (SAXException e) {
			this.releaseSynchronizedXMLConsumer();
			throw e;
		}
	}

	/**
	 * Report the end of a CDATA section.
	 */
	public void endCDATA()
	throws SAXException {
		try {
			this.acquireSynchronizedXMLConsumer();
			if ( this.isAcquired()
					&& super.synchronizedXmlConsumer != null )
			{
				super.synchronizedXmlConsumer.endCDATA();
			}
		} catch (SAXException e) {
			this.releaseSynchronizedXMLConsumer();
			throw e;
		}
	}

	/**
	 * Report an XML comment anywhere in the document.
	 *
	 * @param ch An array holding the characters in the comment.
	 * @param start The starting position in the array.
	 * @param len The number of characters to use from the array.
	 */
	public void comment(char ch[], int start, int len)
	throws SAXException {
		try {
			if ( this.isAcquired()
					&& super.synchronizedXmlConsumer != null )
			{
				super.synchronizedXmlConsumer.comment(ch, start, len);
			}
		} catch (SAXException e) {
			this.releaseSynchronizedXMLConsumer();
			throw e;
		}
	}

}
