/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 1999 The Apache Software Foundation.  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
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xalan" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written 
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * 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 APACHE SOFTWARE FOUNDATION 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.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 * $ Id: $
 *
 */

#include "ExtensionNSHandler.hpp"



#include <cassert>


#include <XalanDOM/XalanElement.hpp>



#include <PlatformSupport/StringTokenizer.hpp>
#include <PlatformSupport/DOMStringHelper.hpp>



#include "StylesheetExecutionContext.hpp"



class XSLTProcessor;



struct XSLProcessorContext
{
	const XSLTProcessor&	m_processor;
	const Stylesheet&		m_stylesheetTree;
	const XalanNode*		m_sourceTree;
	const XalanNode*		m_sourceNode;
	const QName&			m_mode;

	XSLProcessorContext(
			const XSLTProcessor&	processor,
			const Stylesheet&		stylesheetTree, 
			const XalanNode*		sourceTree,
			const XalanNode*		sourceNode,
			const QName&			mode) :
		m_processor(processor),
		m_stylesheetTree(stylesheetTree),
		m_sourceTree(sourceTree),
		m_sourceNode(sourceNode),
		m_mode(mode)
	{
	}
};



/**
 * Construct a new extension namespace handler for a given extension NS.
 * This doesn't do anything - just hang on to the namespace URI.
 * 
 * @param namespaceUri the extension namespace URI that I'm implementing
 */
ExtensionNSHandler::ExtensionNSHandler(const XalanDOMString&	namespaceUri) :
	ExtensionFunctionHandler(namespaceUri),
	m_elements(),
	m_componentDescLoaded(false)
{
}



/**
 * Construct a new extension namespace handler given all the information
 * needed. 
 * 
 * @param xslp				 handle to the XSL processor that I'm working for
 * @param namespaceUri the extension namespace URI that I'm implementing
 * @param elemNames		string containing list of elements of extension NS
 * @param funcNames		string containing list of functions of extension NS
 * @param lang				 language of code implementing the extension
 * @param srcURL			 value of src attribute (if any) - treated as a URL
 *										 or a classname depending on the value of lang. If
 *										 srcURL is not null, then scriptSrc is ignored.
 * @param scriptSrc		the actual script code (if any)
 */
ExtensionNSHandler::ExtensionNSHandler (
			const XalanDOMString& namespaceUri,
			const XalanDOMString& elemNames,
			const XalanDOMString& funcNames,
			const XalanDOMString& lang,
			const XalanDOMString& srcURL,
			const XalanDOMString& src) :
	ExtensionFunctionHandler(namespaceUri, funcNames, lang, srcURL, src),
	m_elements(),
	m_componentDescLoaded(true)
		 
{
	setElements(elemNames);
}



void
ExtensionNSHandler::setFunctions(const XalanDOMString&	funcNames)
{
    ExtensionFunctionHandler::setFunctions(funcNames);

    m_componentDescLoaded = true;
}



void
ExtensionNSHandler::setScript(
			const XalanDOMString&	lang,
			const XalanDOMString&	srcURL,
			const XalanDOMString&	scriptSrc)
{
    ExtensionFunctionHandler::setScript(lang, srcURL, scriptSrc);

    m_componentDescLoaded = true;
}



void
ExtensionNSHandler::setElements(const XalanDOMString&	elemNames)
{
    if (length(elemNames) != 0)
	{
		StringTokenizer		st(elemNames, " \t\n\r", false);

		while (st.hasMoreTokens() == true)
		{
		  m_elements.insert(st.nextToken()); // just stick it in there basically
		}
	
		m_componentDescLoaded = true;
	}
}



bool
ExtensionNSHandler::isElementAvailable(const XalanDOMString&	element) const
{
    // [SUA-005]
    // --- org ---
    // return (m_elements.find(element) != m_elements.end());

    const std::set<XalanDOMString, std::less<XalanDOMString>,
                                   std::allocator<XalanDOMString> >::const_iterator
            c_itr = m_functions.find(element);

    return c_itr != m_functions.end();
}



void
ExtensionNSHandler::processElement(
			StylesheetExecutionContext&		executionContext,
			const XalanDOMString&			localPart,
			const XalanElement*				/* element */,
			Stylesheet&						/* stylesheetTree */, 
			const XalanNode*				/* sourceTree */,
			const XalanNode*				/* sourceNode */,
			const QName&					/* mode */)
{
	const XObject*	result = 0;

	if (!m_componentStarted) 
	{
		try 
		{
			startupComponent();

			ArgVectorType	argv;
			
//			XSLProcessorContext xpc(processor,
//					stylesheetTree, sourceTree, sourceNode, mode);
			/*
				java:
				Vector argv = new Vector (2);
				argv.addElement (xpc);
				argv.addElement (element);
			*/
				// $$$ ToDo: There's no way this will ever work...
//				argv.push_back(static_cast<void *>(&xpc));
//				argv.push_back(static_cast<void *>(&element));
			result = ExtensionFunctionHandler::callFunction (localPart, argv);
		}
		// catch (XPathProcessorException e) 
		catch (...) 
		{
			/*
				e.printStackTrace ();
				throw new XSLProcessorException (e.getMessage (), e);
			*/
			//@@ TODO: Error reporting, or throw
		} 
	}

	if (result != 0) 
	{
		executionContext.outputToResultTree(*result);
	}
}



void
ExtensionNSHandler::startupComponent()
{
	if (!m_componentDescLoaded) 
	{
		try 
		{
			loadComponentDescription();
		}
		catch (...) 
		// catch (Exception e) 
		{
			// throw new XPathProcessorException (e.getMessage (), e);
			//@@ TODO: Error reporting, or throw
		}
	}

	ExtensionFunctionHandler::startupComponent();
}



void
ExtensionNSHandler::loadComponentDescription()
{
	// First try treaing the URI of the extension as a fully qualified
	// class name; if it works then go with treating this an extension
	// implemented in "javaclass" for with that class being the srcURL.
	// forget about setting elements and functions in that case - so if
	// u do extension-{element,function}-available then u get false, 
	// but that's ok.
	assert(0);		// Don't try this in C++
	// @@ How to do this in C++ ??
/*
	try {
		String cname = namespaceUri.startsWith ("class:") ?
										 namespaceUri.substring (6) : namespaceUri;
		Class.forName (cname); // does it load?
		setScript ("javaclass", namespaceUri, null);
		m_componentDescLoaded = true;
		return;
	} catch (Exception e) {
		// oops, it failed .. ok, so this path ain't gonna pan out. shucks.
	}

	// parse the document at the URI of the extension, if any
	URL url = m_XSLProcessor.getURLFromString(namespaceUri,
																	m_XSLProcessor.m_stylesheetRoot.m_baseIdent);
	
	m_XSLProcessor.m_parserLiaison.parse(new InputSource(url.toString()));
	Document compSpec = m_XSLProcessor.m_parserLiaison.getDocument();
	Element componentElement = compSpec.getDocumentElement ();

	// determine the functions and elements of this component
	setElements (componentElement.getAttribute ("elements"));
	setFunctions (componentElement.getAttribute ("functions"));

	// is there an lxslt:script element child? [NOTE THAT THIS IS NOT
	// PROPER NAMESPACE-WISE .. I'll FIX IT LATER. .. Sanjiva 8/20/99.]
	NodeList nl = componentElement.getElementsByTagName ("lxslt:script");
	switch (nl.getLength ()) 
	{
	case 0:
		break;
	case 1:
		Element scriptElem = (Element) nl.item (0);
		String lang = scriptElem.getAttribute ("lang");
		Attr srcURLAttr = scriptElem.getAttributeNode ("src");
		String srcURL = (srcURLAttr == null) ? null : srcURLAttr.getValue ();
		String src = getScriptString (scriptElem);
		setScript (lang, srcURL, src);
		break;
	default:
		throw new XSLProcessorException ("too many <script>s in component");
	}
	m_componentDescLoaded = true;
*/
}



XalanDOMString
ExtensionNSHandler::getScriptString(const XalanElement&		elem)
{
	XalanDOMString strBuf;

	for (const XalanNode*	n = elem.getFirstChild (); n != 0; n = n->getNextSibling())
	{
		switch (n->getNodeType()) 
		{
		case XalanNode::TEXT_NODE:
		case XalanNode::CDATA_SECTION_NODE:
			strBuf += n->getNodeValue();
			break;

		default:
			break;
		}
	}

	return strBuf;
}
