package com.jxml.quick.model.qjml;
import com.jxml.quick.ocm.*;
import com.jxml.quick.*;
import org.xml.sax.*;
import java.util.*;
import java.io.*;

/**
 * Models the abstract element contentModel in QJML.
 * The contentModel elements form a tree under the elements element, and
 * are used to define the element content of a bean element.
 */
public abstract class QMContentModel extends QMCloneable
{
	/**
	 * Models the label attribute on contentModel elements.
	 * The label attribute gives a name to a contentModel element that can then
	 * be referenced by a cref attribute on another contentModel element.
	 * The label attribute is optional.
	 */
	public String label="";

	/**
	 * Models the cref attribute on contentModel elements.
	 * A cref attribute names another contentModel element. 
	 * By this means the attribute values and element content of one 
	 * contentModel element are copied to another.
	 * The cref attribute is optional.
	 */
	public String cref="";

	/**
	 * Models the optional attribute on contentModel elements.
	 * When set to true, the optional attribute indicates that the element content
	 * is not required.
	 * The optional attribute has a default value of false.
	 */
	public boolean optional=false;

	/**
	 * Models the repeating attribute on contentModel elements.
	 * When set true, the repeating attribute indicates that the element content
	 * can occur more than once.
	 * The repeating attribute has a default value of false.
	 */
	public boolean repeating=false;

	/**
	 * Models the aref element.
	 * The aref element contains the name of a field or property which
	 * the containing bean coin extends.
	 * The aref element is optional.
	 */
	public QMARef aref=null;

	/**
	 * Models an access element.
	 * when an aref element is present, it is used to set access.
	 * The access element is optional, and should not be present when the aref element is present.
	 */
	public QMAccess access=null;

	/**
	 * A container for QMRem objects, which model rem elements.
	 * The rem elements are optional and repeating.
	 */
	public ArrayList remList=new ArrayList();

	/**
	 * The QMContentModel object which models the element named by the cref attribute.
	 * This variable is populated when the data model is initialized.
	 */
	public transient QMContentModel content=null;

	/**
	 * Used to prevent multiple initializations.
	 */
	protected transient boolean initialized=false;

	/**
	 * The name of the enclosing coin.
	 */
	public transient QMCoin enclosingCoin=null;

	/**
	 * Returns a deep copy of this object, but in an uninitialized form.
	 */
	public Object clone()
	{
		QMContentModel rv=(QMContentModel)super.clone();
		rv.aref=(QMARef)clone(aref);
		rv.access=(QMAccess)clone(access);
		rv.remList=clone(remList);
		rv.content=null;
		rv.initialized=false;
		return rv;
	}

	/**
	 * Builds the maps.
	 * This method is called during initialization of the data model.
	 */
	public void buildMaps(Map coinMap, Map contentMap, QMCoin coin)
		throws SAXException
	{
		enclosingCoin=coin;
		if (!"".equals(label))
		{
			if (contentMap.containsKey(label))
				throw new SAXException("Duplicate content model label: "+label);
			contentMap.put(label,this);
		}
		if (access!=null)
			access.buildMaps(coinMap,contentMap,coin);
	}

	/**
	 * Resolve references, inheritance.
	 * This method is called during initialization of the data model.
	 */
	public void init(Map coinMap, Map contentMap)
		throws SAXException
	{
		if (initialized)
			return;
		initialized=true;
		if (!"".equals(cref))
		{
			content=(QMContentModel)contentMap.get(cref);
			cref="";
			if (content==null)
				throw new SAXException("Undefined contentModel: "+cref);
			content.init(coinMap,contentMap);
			if (!optional)
				optional=content.optional;
			if (!repeating)
				repeating=content.repeating;
			if (access==null && aref==null && content.access!=null)
			{
				access=(QMAccess)content.access.clone();
				access.addAccessor(coinMap,contentMap,enclosingCoin);
				//aref=content.aref;
			}
		}

		if (aref!=null)
		{
			String name=aref.text;
			access=enclosingCoin.getAccessor(coinMap,"field_"+name);
			if (access==null)
			{
				QMAccess rAccess=enclosingCoin.getAccessor(coinMap,"get_"+name);
				QMAccess wAccess=enclosingCoin.getAccessor(coinMap,"set_"+name);
				if (rAccess==null)
					access=wAccess;
				else if (wAccess==null)
					access=rAccess;
				else if (rAccess==wAccess)
					access=rAccess;
				else if (rAccess.equals(wAccess))
				{
					// create a synthetic combined access
					access=(QMAccess)wAccess.clone();
					((QMProperty)access).read=true;
				}
				else
					throw new SAXException("mixing abstract and non-abstract properties: "+name);
			}
			if (access==null)
			{
				String l=enclosingCoin.getLabel();
				String m="Coin "+l+" has undefined accessor: "+aref.text;
				throw new SAXException(m);
			}
		}

		accessInit(coinMap,contentMap,enclosingCoin);
	}

	/**
	 * Initialize access.
	 */
	public void accessInit(Map coinMap, Map contentMap, QMCoin coin)
		throws SAXException
	{
		if (access!=null)
			access.init(coinMap,contentMap,coin,this);
	}
}
