// copyright => http://www.jxml.com/mdsax/License.txt
//1.1.2

package com.jxml.quick.engine;

import com.jxml.quick.*;
import com.jxml.quick.access.*;
import com.jxml.quick.tf.*;
import com.jxml.quick.recycle.*;
import java.util.*;
import org.xml.sax.SAXException;

public final class QDocImpl implements QDoc
{
    QContext context=null;
    Object root=null;
    long walkCounter=0;
    HashMap idMap=new HashMap();
    QIdentityHash valueMap=new QIdentityHash();
    Mark lastMark=null;
    Object lastValue=null;
    HashMap unresolved=new HashMap();
    long gen;
    public QContextPool pool=null;
    QDoc schema=null;
    String key=null;
    QSoftDocHash docHash=null;
    QIdentityHash metaMap=null;
    ArrayList postParseList=null;
    public ClassLoader cl=null;

    public QDocImpl()
    {
    }

    public QDocImpl(QContext context)
    {
	this.context=context;
    }

    void beginWalk()
    {
	++walkCounter;
	gen=System.currentTimeMillis();
    }


    public Object getRoot()
    {
	return root;
    }

    public void setRoot(Object object)
    {
	root=object;
    }
 
    public void setId(String id, Object value)
	throws QPE
    {
	if (valueMap.get(value)!=null)///////////////////////////////////////////////////////////////////////////////////////
	    return;
	if ("".equals(id))
	    context.throwPE("ID may not be empty");
	Object dup=idMap.put(id,value);
	if (dup!=null)
	    context.throwPE("duplicate ID: "+id);
	lastValue=value;
	lastMark=new Mark(id);
	valueMap.put(value,lastMark);
	QRefHash.add(value,this);			//1.1.2
	if (unresolved==null)
	    return;
	ArrayList puts=(ArrayList)unresolved.remove(id);
	if (puts==null)
	    return;
	int s=puts.size();
	int i;
	for (i=0;i<s;++i)
	    {
		Put put=(Put)puts.get(i);
		put.add(id,value,context);
	    }
    }
    
    Mark getMark(Object value)
    {
	if (value==lastValue)
	    {
		return lastMark;
	    }
	lastMark=(Mark)valueMap.get(value);
	lastValue=value;
	return lastMark;
    }

    public boolean isKnown(Object value)
    {
	return getMark(value)!=null;
    }
    
    public String getId(Object value)
	throws QPE
    {
	if (isKnown(value))
	    {
		Mark m=getMark(value);
		return m.id;
	    }
	if (gen==0)				//1.1.1
	    beginWalk();
	String id="genid_"+(gen++);
	setId(id,value);
	return id;
    }

    public Object getValue(String id)
    {
	return idMap.get(id);
    }
    
    public boolean isMarked(Object value)
    {
	return getMark(value).walkCount==walkCounter;
    }
    
    public void mark(Object value)
    {
	getMark(value).walkCount=walkCounter;
    }
    
    void resolve(String id, 
		 QAccess access, 
		 Object parent,
		 QTargetFactory parentFactory,
		 QElementFactory.QElement element)
	throws QPE
    {
	Object child=idMap.get(id);
	if (child!=null)
	    {
		if (element.matchClasses(child,child)==null)
		    context.throwPE("ref type mismatch: "+id);
		access.add(parent,parentFactory,child,context);
		return;
	    }
	Put put=new Put(access,parent,parentFactory,element,context);
	ArrayList puts=(ArrayList)unresolved.get(id);
	if (puts==null)
	    {
		puts=new ArrayList();
		unresolved.put(id,puts);
	    }
	puts.add(put);
    }
    
    void end()
	throws QPE
    {
	if (unresolved.size()>0)
	    {
		Set ids=unresolved.keySet();
		Iterator ki=ids.iterator();
		String id=(String)ki.next();
		context.throwPE("Unresolved idref: "+id);
	    }
	unresolved=null;
    }

    public QContext allocateContext()
	throws SAXException
    {
	return pool.allocate();
    }

    public void releaseContext(QContext context)
    {
	pool.release(context);
    }

    public QDoc getSchema()
    {
        return schema;
    }

    public void setSchema(QDoc schema)
    {
        this.schema=schema;
    }

    public String getKey()
    {
        return key;
    }

    public void setKey(String key)
    {
        this.key=key;
    }

    public QMeta setMeta(Object key, QMeta value)
    {
	if (metaMap==null)
	    metaMap=new QIdentityHash();
	return (QMeta)metaMap.put(key,value);
    }

    public QMeta getMeta(Object key)
    {
	if (metaMap==null)
	    return null;
	return (QMeta)metaMap.get(key);
    }

    public HRef getHRef(Object key)
    {
        QMeta meta=getMeta(key);
	if (meta instanceof HRef)
	    return (HRef)meta;
	return null;
    }

    public void createHRef(Object key, String schema, String href, String expects)
    {
	setMeta(key,new HRef(schema,href,expects));
    }

    public synchronized QSoftDocHash getDocHash()
    {
	if (docHash==null)
	    docHash=new QSoftDocHash();
	return docHash;
    }

    public void addPostParse(PostParse postParse)
    {
	if (postParseList==null)
	    postParseList=new ArrayList();
	postParseList.add(postParse);
    }

    public void postParse()
	throws QPE
    {
	if (postParseList==null)
	{
	    context=null;
	    return;
	}
	int i;
	for(i=0;i<postParseList.size();++i)
	{
	    PostParse pp=(PostParse)postParseList.set(i,null);
	    pp.postParse(context);
	}
	postParseList=null;
	context=null;
    }
    
    final static class Put
    {
	QAccess access;
	Object parent;
	QTargetFactory parentFactory;
	QElementFactory.QElement element;
	int reserved;
	
	Put(QAccess access, 
	    Object parent,
	    QTargetFactory parentFactory,
	    QElementFactory.QElement element,
	    QContext context)
	    throws QPE
	{
	    this.access=access;
	    this.parent=parent;
	    this.parentFactory=parentFactory;
	    this.element=element;
	    reserved=access.reserve(parentFactory,parent,context);		//1.1.5
	}

	void add(String id, Object child, QContext context)
	    throws QPE
	{
	    if (element.matchClasses(child,child)==null)
		context.throwPE("ref type mismatch: "+id);
	    access.set(reserved,parent,parentFactory,child,context);
	}
    }
    
    public static class Mark
    {
	String id;
	long walkCount=0;
	
	public Mark(String id)
	{
	    this.id=id;
	}
    }
}
