//Updated 3/22/00, version 1.1.1
//Updated 4/10/00, version 1.1.4

package com.jxml.quick.engine;
import com.jxml.quick.*;
import com.jxml.quick.access.*;
import com.jxml.quick.tf.*;
import java.util.*;

public class QCMSeqFactory extends QCMFactory
{
    protected QFilter newFilter()
	throws QE
    {
	return new QCMSeq();
    }

    class QCMSeq extends QCM
    {
	boolean tested=false;

	QCMSeq()
	    throws QE
	{
	}
	
	public State createState()
	{
	    return new SeqState();
	}

	public boolean isOptional()
	{
	    if (tested)
		return optional;
	    tested=true;
	    if (optional)
		return true;
	    int i;
	    int sz=filters.size();
	    for (i=0;i<sz;++i)
		{
		    QCM qcm=(QCM)filters.get(i);
		    if (!qcm.isOptional())
			return false;
		}
	    optional=true;
	    return true;
	}

	public boolean walk(Object object,
			    QTargetFactory parentFactory,
			    QIterator ci,
			    QContext outputContext,
			    boolean opt,
			    boolean isAttribute)
	    throws QPE
	{
	    boolean rs;
	    opt|=isOptional();
	    QIterator g=getIterator(object,parentFactory);
	    if (g!=null)
		ci=g;
	    boolean f=true;
	    int s=filters.size();
	    while (true)
		{
		    boolean none=true; //1.1.1
		    if (ci!=null)
			{
			    Object o=ci.getCurrent();
			    if (o==null)
				{
				    if (!f)
					rs=true;
				    else
					rs=failWalk(outputContext,opt,object);
				    break;
				}
			}
		    int p;
		    for (p=0;p<s;++p)
			{
			    QCM qcm=(QCM)filters.get(p);
			    boolean trial=qcm.walk(object,
						   parentFactory,
						   ci,
						   outputContext,
						   opt&&none,
						   isAttribute);
			    if (trial)
				none=false;
			}
		    if (none)
			{
			    if (f)
				rs=failWalk(outputContext,opt,object);
			    else
				rs=true;
			    break;
			}
		    if (!repeating)
			{
			    rs=true;
			    break;
			}
		    f=false;									//1.1.4
		}
	    if (g!=null)
	    {                                                                                 //1.1.1
		if (g.getCurrent()!=null)
		{
		    context.throwPE("Unexpressed: "+g.getCurrent().getClass().getName());
		
		}
		access.releaseIterator(g);
	    }
	    return rs;
	}

	class SeqState extends State
	{
	    int pos;
	    final int sz=filters.size();
	    State state=null;

	    public void reset()
	    {
		super.reset();
		pos=-1;
	    }

	    public boolean match(String name, 
				 boolean opt,
				 QAccess mAccess,
				 Object parent,
				 QTargetFactory parentFactory,
				 QIterator cIterator)
		throws QPE
	    {
		QAccess cAccess;
		if (access==null)
		    cAccess=mAccess;
		else
		    cAccess=access;
		if (iterator==null && (cAccess==null || !cAccess.writeable(parentFactory,context)))
		    {
			iterator=getIterator(parent,parentFactory);
			if (iterator==null)
			    iterator=cIterator;
		    }
		opt|=isOptional();
		if (sz==0)
		    return failMatch(opt,name);
		QCM qcm;
		boolean none=false;
		boolean forceReset=false;
		boolean trial;
		do
		    {
			if (pos==-1)
			    {
				none=true;
				pos=0;
				forceReset=true;
			    }
			qcm=(QCM)filters.get(pos);
			state=qcm.getState();
			if (forceReset)
			    state.reset();
			trial=state.match(name,
					  opt&&none,
					  cAccess,
					  parent,
					  parentFactory,
					  iterator);
			if (trial)
			    {
				first=false;
				return trial;
			    }
			forceReset=true;
			++pos;
		    } while (pos<sz);
		pos=-1;
		if (!repeating||none)
		    return failMatch(opt||isComplete(),name);
		none=false;
		do
		    {
			if (pos==-1)
			    {
				none=true;
				pos=0;
				forceReset=true;
			    }
			qcm=(QCM)filters.get(pos);
			state=qcm.getState();
			if (forceReset)
			    state.reset();
			trial=state.match(name,
					  none,
					  cAccess,
					  parent,
					  parentFactory,
					  iterator);
			if (trial)
			    {
				first=false;
				return trial;
			    }
			forceReset=true;
			++pos;
		    } while (pos<sz);
		pos=-1;
		return false;
	    }

	    public boolean isComplete()
	    {
		if (pos==-1)
		    return !first || isOptional();
		if (!state.isComplete())
		    return false;
		int i;
		for (i=pos+1;i<sz;++i)
		    {
			QCM qcm=(QCM)filters.get(i);
			if (!qcm.isOptional())
			    return false;
		    }
		return true;
	    }
	}
    }
}
