/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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.
 */
/*
 * $Id$
 */
package org.apache.xpath.functions.string;

import javax.xml.transform.TransformerException;

import org.apache.xalan.res.XSLMessages;
import org.apache.xalan.xslt.util.XslTransformEvaluationHelper;
import org.apache.xpath.Expression;
import org.apache.xpath.XPathContext;
import org.apache.xpath.axes.SelfIteratorNoPredicate;
import org.apache.xpath.functions.Function;
import org.apache.xpath.functions.FunctionMultiArgs;
import org.apache.xpath.functions.WrongNumberArgsException;
import org.apache.xpath.objects.XObject;
import org.apache.xpath.objects.XString;

import xml.xpath31.processor.types.XSAnyType;
import xml.xpath31.processor.types.XSUntyped;
import xml.xpath31.processor.types.XSUntypedAtomic;

/**
 * Implementation of XPath 3.1 function fn:concat.
 * 
 * @xsl.usage advanced
 */
public class FuncConcat extends FunctionMultiArgs
{
	static final long serialVersionUID = 1737228885202314413L;

	private int m_min_arity = 2;

	private int m_max_arity = Integer.MAX_VALUE - 1;
	
	private int m_defined_arity = 0;
	
	/**
	 * Class constructor.
	 */
	public FuncConcat() {
		// no op
	}

	/**
	 * Evaluate the function. The function must return
	 * a valid object.
	 * 
	 * @param xctxt The current execution context.
	 * @return A valid XObject.
	 *
	 * @throws javax.xml.transform.TransformerException
	 */
	public XObject execute(XPathContext xctxt) throws javax.xml.transform.TransformerException
	{

		StringBuffer sb = new StringBuffer();        

		// Compiler says we must have at least two arguments
		sb.append(inspectXPathSelfAxesExpression(m_arg0, xctxt));
		sb.append(inspectXPathSelfAxesExpression(m_arg1, xctxt));

		if (m_arg2 != null) {
			sb.append(inspectXPathSelfAxesExpression(m_arg2, xctxt));
		}

		if (m_args != null) {
			for (int i = 0; i < m_args.length; i++) {
				sb.append(inspectXPathSelfAxesExpression(m_args[i], xctxt));
			}
		}

		return new XString(sb.toString());
	}

	/**
	 * Check that the number of arguments passed to this function is correct.
	 *
	 * @param argNum The number of arguments that is being passed to the function.
	 *
	 * @throws WrongNumberArgsException
	 */
	public void checkNumberArgs(int argNum) throws WrongNumberArgsException
	{
		if (argNum < 2)
			reportWrongNumberArgs();
	}

	/**
	 * Constructs and throws a WrongNumberArgException with the appropriate
	 * message for this function object.
	 *
	 * @throws WrongNumberArgsException
	 */
	protected void reportWrongNumberArgs() throws WrongNumberArgsException {
		throw new WrongNumberArgsException(XSLMessages.createXPATHMessage("gtone", null));
	}

	/**
	 * When, an XPath expression's pattern string is ".", the evaluation result of 
	 * XPath expression, is the string value of XPath context item.
	 * 
	 * Other than, handling an XPath expression that has a pattern string as ".", 
	 * this method does few of other things as well. 
	 */
	private String inspectXPathSelfAxesExpression(Expression expr, XPathContext xctxt) 
																					throws TransformerException {
		String resultStr = null;

		if (expr instanceof SelfIteratorNoPredicate) {
			XObject xpath3ContextItem = xctxt.getXPath3ContextItem();    	  
			if (xpath3ContextItem != null) {
				resultStr = XslTransformEvaluationHelper.getStrVal(xpath3ContextItem);
			}
			else {
				XObject xObj = expr.execute(xctxt);
				resultStr = XslTransformEvaluationHelper.getStrVal(xObj);   
			}
		}
		else if (expr instanceof Function) {
			XObject evalResult = ((Function)expr).execute(xctxt);
			resultStr = XslTransformEvaluationHelper.getStrVal(evalResult);
		}
		else if (expr instanceof XSUntyped || expr instanceof XSUntypedAtomic) {
			resultStr = ((XSAnyType)expr).stringValue();
		}
		else {
			XObject xObj = expr.execute(xctxt);
			resultStr = XslTransformEvaluationHelper.getStrVal(xObj);
		}

		return resultStr;      
	}

	public int getMinArity() {
		return m_min_arity;
	}

	public void setMinArity(int minArity) {
		this.m_min_arity = minArity;
	}

	public int getMaxArity() {
		return m_max_arity;
	}

	public void setMaxArity(int maxArity) {
		this.m_max_arity = maxArity;
	}
	
	public int getActualArity() {
	    return m_defined_arity; 
	}
	  
	public void setActualArity(int definedArity) {
	    this.m_defined_arity = definedArity; 
	}  
}
