//Updated 2/19/00, version 1.0.1
//Updated 3/22/00, version 1.1.1
//Updated 3/24/00, version 1.1.2
//Updated 3/30/00, version 1.1.4

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

public class QCElementFactory extends QCMFactory
{
    public String defaultValue="";
    public boolean fixed=false;

    protected QFilter newFilter()
	throws QE
    {
	return new QCElement();
    }

    final class QCElement extends QCM
    {
	QCElement()
	    throws QE
	{
	}

	public void init()
	    throws QE
	{
	    super.init();
	    if (filters.size()==0)
		throw new QE("QCElementFactory requires sub-factory");
	}
	
	public State createState()
	{
	    return new CEState();
	}
	
	public boolean walk(Object object,
			    QTargetFactory parentFactory,
			    QIterator ci,
			    QContext outputContext,
			    boolean opt,
			    boolean isAttribute)
	    throws QPE
	{
	    boolean rs;
	    opt|=optional;
	    QIterator g=getIterator(object,parentFactory);
	    if (g!=null)
		ci=g;
	    if (ci==null)
		{
		    System.out.println("object class="+object.getClass().getName());
		    System.out.println("parentFactory="+parentFactory.getClass().getName());
		    System.out.println("No iterator!");
		    rs=failWalk(outputContext,false,object);			//1.1.4
		}
	    else
		{
		    QElementFactory.QElement element=
			(QElementFactory.QElement)filters.get(0);
		    boolean f=true;
		    while (true)
			{
			    Object o=ci.getCurrent();
			    if (o==null)
				{
				    if (!f)
					rs=true;
				    else
					rs=failWalk(outputContext,opt,object);
				    break;
				}
			    else if (fixed)						//1.1.2
			    {
			        ci.getNext();
			        f=false;
			        if (!repeating)
				{
				    rs=true;
				    break;
				}
			    }
			    else
				{
				    QDoc doc=context.doc;
				    Object oold=o;
				    QMeta t=doc.getMeta(o);
				    if (t!=null)
				    {
					o=t.getWalkObject(object,o,doc);
				    }
				    else if (!isAttribute)
				    {							//1.1.2
				        if (doc.isKnown(o))
					{
					    if (doc.isMarked(o))
					    {
						String id=doc.getId(o);
						String schemaKey=doc.getSchema().getKey();
						doc.createHRef(o,schemaKey,"#"+id,"java.lang.Object");
						t=doc.getMeta(o);
						o=t;
					    }
					}
					else if (o!=doc.getRoot())	
					{
					    QRefHash.createHRef(doc,o);
					    t=doc.getMeta(o);
					    if (t!=null)
					    {
						o=t;
					    }
					}
				    }
				    QElementFactory.QElement e=element.matchClasses(o,oold);
				    if (e==null)
					{
					    if (!f)
						rs=true;
					    else
						rs=failWalk(outputContext,opt,o);
					    break;
					}
				    else
					{
					    if (e.isAttList())
					    {
					        e.walk(oold,oold,parentFactory,outputContext,isAttribute,defaultValue); //Updated 2/19/00, version 1.0.1
					    }
					    else
					    {
					        if (e.isMeta)		//1.1.1
						{
						    o=t;
						}
					        e.walk(o,oold,parentFactory,outputContext,isAttribute,defaultValue); //Updated 2/19/00, version 1.0.1
					    }
					    ci.getNext();
					    f=false;
					    if (!repeating)
					    {
						rs=true;
						break;
					    }
					}
				}
			}
		}
	    if (g!=null)
	    {                                                                                 //1.1.1
		if (g.getCurrent()!=null && !isAttribute)				//1.1.5
		{
		   //context.throwPE("Unexpressed: "+g.getCurrent().getClass().getName());
		}
		access.releaseIterator(g);
	    }
	    return rs;
	}

        public void processFixed(Object parent, 
                                 QTargetFactory parentFactory)
		throws QPE
        {
	     if (!fixed)
		return;
	    QElementFactory.QElement element=
		    (QElementFactory.QElement)filters.get(0);
	    element.processFixed(parent,parentFactory,defaultValue,access);
	}

	//	25-dec-00
        public void processDefault(Object parent,
                                 QTargetFactory parentFactory)
		throws QPE
        {
		if (fixed)
			return;
		if ("".equals(defaultValue))
			return;
	    QElementFactory.QElement element=
		    (QElementFactory.QElement)filters.get(0);
	    element.processDefault(parent,parentFactory,defaultValue,access);
	}

	final class CEState extends State
	{
	    QAccess cAccess;

	    public boolean match(String name, 
				 boolean opt,
				 QAccess mAccess,
				 Object parent,
				 QTargetFactory parentFactory,
				 QIterator cIterator)
		throws QPE
	    {
		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|=optional;
		if (!first && !repeating)
		    return false;
		QElementFactory.QElement element=
		    (QElementFactory.QElement)filters.get(0);
		QEI eState=element.matchTags(name);
		boolean fake=false;
		if (eState==null)
		    {
			if (defaultValue.equals(""))
			{
				if (!opt&&first)
				{
				    System.err.println("parent="+parent);
				    System.err.println("opt="+opt);
				    System.err.println("first="+first);
				}
			    return failMatch(opt|!first,name); //1.1.1
			}
			//if (fixed)	25-dec-00
			    return false;
			//fake=true;	25-dec-00
			//eState=(QEI)element.getState();	25-dec-00
		    }
		ArrayList elements=((QContextImpl)context).elements;
		int ndx=((QContextImpl)context).ndx;
		while (elements.size()<(ndx+1))
		    elements.add(eState);
		elements.set(ndx,eState);
		first=false;
		if (fixed && !fake)
		    eState.processStart(cAccess,parentFactory,name,iterator,defaultValue);
		else
		    eState.processStart(cAccess,parentFactory,name,iterator,null);
		if (!fake)
		    return true;
		eState.processEnd(defaultValue);
		return false;
	    }
	}
    }
}
