/**
 * ***************************************************************
 * JADE - Java Agent DEvelopment Framework is a framework to develop
 * multi-agent systems in compliance with the FIPA specifications.
 * Copyright (C) 2000 CSELT S.p.A.
 * 
 * GNU Lesser General Public License
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation,
 * version 2.1 of the License.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA  02111-1307, USA.
 * **************************************************************
 */
package jade.content.abs;

//#APIDOC_EXCLUDE_FILE

import jade.content.onto.*;
import jade.content.*;
import jade.content.schema.*;
import jade.util.leap.HashSet;
import jade.util.leap.List;
import jade.util.leap.ArrayList;
import jade.util.leap.Iterator;
import jade.util.leap.Set;
import jade.core.AID;
import jade.core.CaseInsensitiveString;
import jade.lang.acl.ACLMessage;


/**
 * @author Federico Bergenti - Universita` di Parma
 * @author Giovanni Caire - TILAB
 */
public class AbsHelper {
	/**
	 * Converts a <code>List</code> into a <code>AbsAggregate</code> using
	 * the specified ontology.
	 * @param obj the <code>List</code>
	 * @param onto the ontology.
	 * @return the abstract descriptor.
	 * @throws OntologyException
	 */
	public static AbsAggregate externaliseList(List obj, Ontology onto, String AggregateType) throws OntologyException {
		AbsAggregate ret = new AbsAggregate(AggregateType);

		try {
			for (int i = 0; i < obj.size(); i++) {
				ret.add((AbsTerm) (onto.fromObject(obj.get(i))));
			}
		}
		catch (ClassCastException cce) {
			throw new OntologyException("Non term object in aggregate");
		}

		return ret;
	} 

	/**
	 * Converts a <code>Set</code> into a <code>AbsAggregate</code> using
	 * the specified ontology.
	 * @param obj the <code>Set</code>
	 * @param onto the ontology.
	 * @return the abstract descriptor.
	 * @throws OntologyException
	 */
	public static AbsAggregate externaliseSet(Set obj, Ontology onto, String AggregateType) throws OntologyException {
		AbsAggregate ret = new AbsAggregate(AggregateType);

		try {
			Iterator it = obj.iterator();
			while(it.hasNext()) {
				ret.add((AbsTerm) (onto.fromObject(it.next())));
			}
		}
		catch (ClassCastException cce) {
			throw new OntologyException("Non term object in aggregate");
		}

		return ret;
	} 

	/**
	 * Converts an <code>Iterator</code> into a <code>AbsAggregate</code> using
	 * the specified ontology.
	 * @param obj the <code>Iterator</code>
	 * @param onto the ontology.
	 * @return the abstract descriptor.
	 * @throws OntologyException
	 */
	//#J2ME_EXCLUDE_BEGIN
	public static AbsAggregate externaliseIterator(java.util.Iterator obj, Ontology onto, String AggregateType) throws OntologyException {
		//#J2ME_EXCLUDE_END
		/*#J2ME_INCLUDE_BEGIN
        public static AbsAggregate externaliseIterator(Iterator obj, Ontology onto, String AggregateType) throws OntologyException {
        #J2ME_INCLUDE_END*/
		AbsAggregate ret = new AbsAggregate(AggregateType);

		try {
			while(obj.hasNext())
				ret.add((AbsTerm) (onto.fromObject(obj.next())));
		}
		catch (ClassCastException cce) {
			throw new OntologyException("Non term object in aggregate");
		}
		return ret;
	}

	/**
	 * Converts an <code>AID</code> into an <code>AbsConcept</code> 
	 * representing an AID 
	 * @param obj the <code>AID</code>
	 * @return the abstract descriptor.
	 */
	public static AbsConcept externaliseAID(AID obj) {
		AbsConcept aid = new AbsConcept(BasicOntology.AID);
		// Name
		aid.set(BasicOntology.AID_NAME, obj.getName());

		// Addresses
		Iterator i = obj.getAllAddresses();
		if (i.hasNext()) {
			AbsAggregate addresses = new AbsAggregate(BasicOntology.SEQUENCE);
			while (i.hasNext()) {
				String addr = (String) i.next();
				addresses.add(AbsPrimitive.wrap(addr));
			}
			aid.set(BasicOntology.AID_ADDRESSES, addresses);
		}
		// Resolvers
		i = obj.getAllResolvers();
		if (i.hasNext()) {
			AbsAggregate resolvers = new AbsAggregate(BasicOntology.SEQUENCE);
			while (i.hasNext()) {
				AID res = (AID) i.next();
				resolvers.add(externaliseAID(res));
			}
			aid.set(BasicOntology.AID_RESOLVERS, resolvers);
		}
		return aid;
	} 

	/**
	 * Converts a <code>ContentElementList</code> into an
	 * <code>AbsContentElementList</code> using
	 * the specified ontology.
	 * @param obj the <code>ContentElementList</code>
	 * @param onto the ontology.
	 * @return the abstract descriptor.
	 * @throws OntologyException
	 */
	public static AbsContentElementList externaliseContentElementList(ContentElementList obj, Ontology onto) throws OntologyException {
		AbsContentElementList ret = new AbsContentElementList();

		try {
			for (int i = 0; i < obj.size(); i++) {
				ret.add((AbsContentElement) (onto.fromObject(obj.get(i))));
			}
		}
		catch (ClassCastException cce) {
			throw new OntologyException("Non content element object in content element list");
		}

		return ret;
	} 

	/**
	 * Converts an <code>ACLMessage</code> into an
	 * <code>AbsAgentAction</code> using
	 * the specified ontology.
	 * @param obj the <code>ACLMessage</code>
	 * @param onto the ontology.
	 * @return the abstract descriptor.
	 * @throws OntologyException
	 */
	public static AbsAgentAction externaliseACLMessage(ACLMessage obj, Ontology onto) throws OntologyException {
		try {
			AbsAgentAction absMsg =  new AbsAgentAction(ACLMessage.getPerformative(obj.getPerformative()));

			absMsg.set(BasicOntology.ACLMSG_SENDER, (AbsTerm) onto.fromObject(obj.getSender()));
			// Receivers
			AbsAggregate recvs = new AbsAggregate(BasicOntology.SEQUENCE);
			Iterator it = obj.getAllReceiver();
			while (it.hasNext()) {
				recvs.add((AbsTerm) onto.fromObject(it.next()));
			}
			if (recvs.size() > 0) {
				absMsg.set(BasicOntology.ACLMSG_RECEIVERS, recvs);
			}
			// Reply_to
			AbsAggregate repls = new AbsAggregate(BasicOntology.SEQUENCE);
			it = obj.getAllReplyTo();
			while (it.hasNext()) {
				repls.add((AbsTerm) onto.fromObject(it.next()));
			}
			if (repls.size() > 0) {
				absMsg.set(BasicOntology.ACLMSG_REPLY_TO, repls);
			}

			absMsg.set(BasicOntology.ACLMSG_LANGUAGE, obj.getLanguage());
			absMsg.set(BasicOntology.ACLMSG_ONTOLOGY, obj.getOntology());
			absMsg.set(BasicOntology.ACLMSG_PROTOCOL, obj.getProtocol());
			absMsg.set(BasicOntology.ACLMSG_IN_REPLY_TO, obj.getInReplyTo());
			absMsg.set(BasicOntology.ACLMSG_REPLY_WITH, obj.getReplyWith());
			absMsg.set(BasicOntology.ACLMSG_CONVERSATION_ID, obj.getConversationId());
			absMsg.set(BasicOntology.ACLMSG_REPLY_BY, obj.getReplyByDate());
			// Content
			if (obj.hasByteSequenceContent()) {
				absMsg.set(BasicOntology.ACLMSG_BYTE_SEQUENCE_CONTENT, obj.getByteSequenceContent());
			}
			else {
				absMsg.set(BasicOntology.ACLMSG_CONTENT, obj.getContent());
			}
			absMsg.set(BasicOntology.ACLMSG_ENCODING, obj.getEncoding());

			return absMsg;
		}
		catch (Exception e) {
			throw new OntologyException("Error externalising ACLMessage", e);
		}
	}


	/**
	 * Converts an <code>AbsAggregate</code> into a List using the 
	 * specified ontology.
	 * @param onto the ontology
	 * @return the List
	 * @throws OntologyException
	 */
	public static List internaliseList(AbsAggregate aggregate, Ontology onto) throws OntologyException {
		List ret = new ArrayList();

		for (int i = 0; i < aggregate.size(); i++) {
			Object element = onto.toObject(aggregate.get(i));
			// Check if the element is a Term, a primitive an AID or a List
			Ontology.checkIsTerm(element);
			ret.add(element);
		}

		return ret;
	} 

	/**
	 * Converts an <code>AbsAggregate</code> into a Set using the 
	 * specified ontology.
	 * @param onto the ontology
	 * @return the Set
	 * @throws OntologyException
	 */
	public static Set internaliseSet(AbsAggregate aggregate, Ontology onto) throws OntologyException {
		Set ret = new HashSet();

		for (int i = 0; i < aggregate.size(); i++) {
			Object element = onto.toObject(aggregate.get(i));
			// Check if the element is a Term, a primitive an AID or a List
			Ontology.checkIsTerm(element);
			ret.add(element);
		}

		return ret;
	} 

	//#MIDP_EXCLUDE_BEGIN
	/**
	 * Converts an <code>AbsAggregate</code> into a List using the 
	 * specified ontology.
	 * @param onto the ontology
	 * @return the List
	 * @throws OntologyException
	 */
	public static java.util.Collection internaliseJavaCollection(AbsAggregate aggregate, Ontology onto) throws OntologyException {
		java.util.Collection collection;

		try {
			collection = (java.util.Collection) Class.forName(aggregate.getTypeName()).newInstance();
		} catch (Exception e) {
			throw new OntologyException("Cannot instantiate java collection of class "+aggregate.getTypeName(), e);
		}
		for (int i = 0; i < aggregate.size(); i++) {
			Object element = onto.toObject(aggregate.get(i));
			// Check if the element is a Term, a primitive an AID or a List
			Ontology.checkIsTerm(element);
			collection.add(element);
		}

		return collection;
	} 
	//#MIDP_EXCLUDE_END

	/**
	 * Converts an <code>AbsConcept</code> representing an AID 
	 * into an OntoAID 
	 * @return the OntoAID
	 * @throws OntologyException if <code>aid</code> does not 
	 * represent a valid AID
	 */
	public static OntoAID internaliseAID(AbsConcept aid) throws OntologyException {
		OntoAID ret = new OntoAID();

		try {
			// Name
			ret.setName(aid.getString(BasicOntology.AID_NAME));

			// Addresses
			AbsAggregate addresses = (AbsAggregate) aid.getAbsObject(BasicOntology.AID_ADDRESSES);
			if (addresses != null) {
				for (int i = 0; i < addresses.size(); ++i) {
					String addr = ((AbsPrimitive) addresses.get(i)).getString();
					ret.addAddresses(addr);
				}
			}
			// Resolvers
			AbsAggregate resolvers = (AbsAggregate) aid.getAbsObject(BasicOntology.AID_RESOLVERS);
			if (resolvers != null) {
				for (int i = 0; i < resolvers.size(); ++i) {
					OntoAID res = internaliseAID((AbsConcept) resolvers.get(i));
					ret.addResolvers(res);
				}
			}
			return ret;
		}
		catch (Exception e) {
			throw new OntologyException(aid+" is not a valid AID");
		}
	} 

	/**
	 * Converts to an <code>AbsContentElementList</code> into a 
	 * ContentElementList using the 
	 * specified ontology.
	 * @param onto the ontology
	 * @return the ContentElementList
	 * @throws OntologyException
	 */
	public static ContentElementList internaliseContentElementList(AbsContentElementList l, Ontology onto) throws OntologyException {
		ContentElementList ret = new ContentElementList();

		try {
			for (int i = 0; i < l.size(); i++) {
				ContentElement element = (ContentElement) onto.toObject(l.get(i));
				ret.add(element);
			}
		}
		catch (ClassCastException cce) {
			throw new OntologyException("Non content element object in content element list");
		}

		return ret;
	} 

	/**
	 * Converts to an <code>AbsAgentAction</code> representing an ACLMessage
	 * into an OntoACLMessage using the specified ontology.
	 * @param onto the ontology
	 * @return the OntoACLMessage
	 * @throws OntologyException
	 */
	public static OntoACLMessage internaliseACLMessage(AbsAgentAction absMsg, Ontology onto) throws OntologyException {
		OntoACLMessage ret = new OntoACLMessage(ACLMessage.getInteger(absMsg.getTypeName()));

		try {
			ret.setSender((AID) onto.toObject(absMsg.getAbsObject(BasicOntology.ACLMSG_SENDER))); 
			// Receivers
			ret.clearAllReceiver();
			List l = (List) onto.toObject(absMsg.getAbsObject(BasicOntology.ACLMSG_RECEIVERS));
			if (l != null) {
				Iterator it = l.iterator();
				while (it.hasNext()) {
					ret.addReceiver((AID) it.next());
				}
			}
			// ReplyTo
			ret.clearAllReplyTo();
			l = (List) onto.toObject(absMsg.getAbsObject(BasicOntology.ACLMSG_REPLY_TO));
			if (l != null) {
				Iterator it = l.iterator();
				while (it.hasNext()) {
					ret.addReplyTo((AID) it.next());
				}
			}
			ret.setLanguage(absMsg.getString(BasicOntology.ACLMSG_LANGUAGE)); 
			ret.setOntology(absMsg.getString(BasicOntology.ACLMSG_ONTOLOGY)); 
			ret.setProtocol(absMsg.getString(BasicOntology.ACLMSG_PROTOCOL)); 
			ret.setInReplyTo(absMsg.getString(BasicOntology.ACLMSG_IN_REPLY_TO)); 
			ret.setReplyWith(absMsg.getString(BasicOntology.ACLMSG_REPLY_WITH)); 
			ret.setConversationId(absMsg.getString(BasicOntology.ACLMSG_CONVERSATION_ID)); 
			ret.setReplyByDate(absMsg.getDate(BasicOntology.ACLMSG_REPLY_BY)); 
			String c = absMsg.getString(BasicOntology.ACLMSG_CONTENT);
			if (c != null) {
				ret.setContent(c);
			}
			else {
				byte[] bsc = absMsg.getByteSequence(BasicOntology.ACLMSG_BYTE_SEQUENCE_CONTENT);
				if (bsc != null) {
					ret.setByteSequenceContent(bsc);
				}
			}
			ret.setEncoding(absMsg.getString(BasicOntology.ACLMSG_ENCODING)); 

			return ret;
		}
		catch (Exception e) {
			throw new OntologyException("Error internalising OntoACLMessage", e);
		}
	}

	/**
	 * Set an attribute in an abstract descriptor performing all 
	 * necessary type checks.
	 * @throws OntologyException if a type mismatch is detected
	 */
	public static void setAttribute(AbsObject abs, String attrName, AbsObject attrValue) throws OntologyException { 
		if (abs.getAbsType() == AbsObject.ABS_AGENT_ACTION) {
			if (attrValue instanceof AbsTerm || attrValue == null) {
				((AbsAgentAction) abs).set(attrName, (AbsTerm) attrValue);
				return;
			}
			if (attrValue instanceof AbsPredicate || attrValue == null) {
				((AbsAgentAction) abs).set(attrName, (AbsPredicate) attrValue);
				return;
			}
		}
		if (abs.getAbsType() == AbsObject.ABS_CONCEPT) {
			if (attrValue instanceof AbsTerm || attrValue == null) {
				((AbsConcept) abs).set(attrName, (AbsTerm) attrValue);
				return;
			}
		}
		else if (abs.getAbsType() == AbsObject.ABS_PREDICATE) {
			((AbsPredicate) abs).set(attrName, attrValue);
			return;
		}
		else if (abs.getAbsType() == AbsObject.ABS_IRE) {
			if (attrValue instanceof AbsVariable && CaseInsensitiveString.equalsIgnoreCase(attrName, IRESchema.VARIABLE)) {
				((AbsIRE) abs).setVariable((AbsVariable) attrValue);
				return;
			}
			else if (attrValue instanceof AbsPredicate && CaseInsensitiveString.equalsIgnoreCase(attrName, IRESchema.PROPOSITION)) {
				((AbsIRE) abs).setProposition((AbsPredicate) attrValue);
				return;
			}
		}
		else if (abs.getAbsType() == AbsObject.ABS_VARIABLE) {
			if (attrValue instanceof AbsPrimitive && CaseInsensitiveString.equalsIgnoreCase(attrName, VariableSchema.NAME)) {
				((AbsVariable) abs).setName(((AbsPrimitive) attrValue).getString());
				return;
			}
			else if (attrValue instanceof AbsPrimitive && CaseInsensitiveString.equalsIgnoreCase(attrName, VariableSchema.VALUE_TYPE)) {
				((AbsVariable) abs).setType(((AbsPrimitive) attrValue).getString());
				return;
			}
		}

		// If we reach this point there is a type incompatibility
		throw new OntologyException("Type incompatibility: value of attribute "+attrName+" of "+abs+" is "+attrValue); 
	}

	
	//#J2ME_EXCLUDE_BEGIN
	/**
	 * Remove all variables and empty aggregates (only if specified)
	 * @param abs to nullify
	 * @param removeEmptyAggregate if true remove all empty aggregates  
	 * @return abs without variables and empty aggregates
	 */
	public static AbsObject nullifyVariables(AbsObject abs, boolean removeEmptyAggregate) {
		// Remove AbsVariable
		if (abs instanceof AbsVariable) {
			return null;
		}
		// Remove empty AbsAggregate
		if (removeEmptyAggregate && abs instanceof AbsAggregate) {
			AbsAggregate absAggregate = (AbsAggregate)abs;
			if (absAggregate.size() == 0) {
				return null;
			}
		}
		// If not grounded -> check all slots
		if (!abs.isGrounded()) {
			
			// Aggregate
			if (abs instanceof AbsAggregate) {
				AbsAggregate absAggregate = (AbsAggregate)abs;
				
				Iterator it = absAggregate.iterator();
				while (it.hasNext()) {
					AbsObject slotValue = (AbsObject)it.next();
					
					AbsObject nullifiedSlotValue = nullifyVariables(slotValue, removeEmptyAggregate);
					if (nullifiedSlotValue == null) {
						// Remove null slot
						it.remove();
					}
				}
				if (removeEmptyAggregate && abs.getCount() == 0) {
					return null;
				}
			} 
			// Concept
			else {
				for (String slotName : abs.getNames()) {
					AbsObject slotValue = abs.getAbsObject(slotName);
					AbsObject nullifiedSlotValue = nullifyVariables(slotValue, removeEmptyAggregate);
					
					// Replace nullified value into the slot
					((AbsConcept)abs).set(slotName, nullifiedSlotValue);
				}
			}
		}
		return abs;
	}
	
	/**
	 * Recursively removes prefix (if present) from abs-type-name, except for BasicOntology types
	 * Prefix is identified from last separator string
	 * Eg. A.B.C -> C (if separator is .)
	 * @param abs abs to un-prefix
	 * @param separator separator string
	 * @return un-prefixde abs
	 * @throws OntologyException
	 */
	public static AbsObject removePrefix(AbsObject abs, String separator) throws OntologyException {
		if (abs instanceof AbsConcept || abs instanceof AbsAggregate) {
			String typeName = abs.getTypeName();
			
			// If the abs schema is a BasicOntology schema don't try to remove prefix
			// ex. SEQUENCE
			if (BasicOntology.getInstance().getSchema(typeName) == null) {
				
				// Check if separator is present
				int separatorPos = typeName.lastIndexOf(separator);
				if (separatorPos != -1 && separatorPos < typeName.length()) {
					// Remove prefix 
					typeName = typeName.substring(separatorPos+separator.length());
	
					// Set un-prefixed typeName  
					((AbsConcept)abs).setTypeName(typeName);
				}
			}
			
			for (String slotName : abs.getNames()) {
				removePrefix(abs.getAbsObject(slotName), separator);
			}
		}
		return abs;
	}
	
	/**
	 * Recursively add prefix to abs-type-name, except for BasicOntology types
	 * Eg. C -> A.B.C (if prefix is A.B.)
	 * @param abs abs to prefix
	 * @param prefix prefix to add
	 * @return prefix abs
	 * @throws OntologyException
	 */
	public static AbsObject addPrefix(AbsObject abs, String prefix) throws OntologyException {
		if (abs instanceof AbsConcept || abs instanceof AbsAggregate) {
			String typeName = abs.getTypeName();
			
			// If the abs schema is a BasicOntology schema don't add prefix
			// ex. SEQUENCE
			if (BasicOntology.getInstance().getSchema(typeName) == null) {
				((AbsConcept)abs).setTypeName(prefix+typeName);
			}
			
			for (String slotName : abs.getNames()) {
				addPrefix(abs.getAbsObject(slotName), prefix);
			}
		}
		return abs;
	}
	
	/**
	 * Return true if the abs-object is a template.
	 * A template is an abs with all slots AbsVariable or AbsAggregate empty
	 * @param abs abs-object to check
	 * @return true if is a template
	 */
	public static boolean isAbsTemplate(AbsObject abs) {
		if (abs instanceof AbsPrimitive) {
			return false;
		}
		
		if (abs instanceof AbsAggregate) {
			Iterator it = ((AbsAggregate)abs).iterator();
			while (it.hasNext()) {
				if (!isAbsTemplate((AbsObject)it.next())) {
					return false;
				}
			}
		}

		if (abs instanceof AbsConcept) {
			for (String slotName : abs.getNames()) {
				if (!isAbsTemplate(abs.getAbsObject(slotName))) {
					return false;
				}
			}
		}

		return true;
	}
	
	/**
	 * Apply default value to template
	 * Replace node with default value only if the value in template is null or AbsVariable or 
	 * default value is null and template is a true template 
	 * @param absTemplate abs template value 
	 * @param absDefault abs value with default value
	 * @return
	 * @throws OntologyException 
	 */
	public static AbsObject applyDefaultValues(AbsObject absTemplate, AbsObject absDefault) throws OntologyException {

		// Aggregate not empty but all filled -> ignore default
		if (absTemplate instanceof AbsAggregate && absTemplate.getCount() != 0 && absTemplate.isGrounded()) {
			return absTemplate;
		}
		
		// Template is already all filled -> ignore default
		if (!(absTemplate instanceof AbsAggregate) && absTemplate.isGrounded()) {
			return absTemplate; 
		}
		
		// Template slot null or variable -> replace with default 
		if (absTemplate == null || absTemplate instanceof AbsVariable) {
			return absDefault; 
		}

		// Manage default value null  
		if (absDefault == null) {
			if (isAbsTemplate(absTemplate)) {
				return null;
			} else {
				return absTemplate;
			}
		}

		// Here the template is a concept -> check compatibility 
		if (!(absDefault instanceof AbsConcept)) {
			throw new OntologyException("Default abs structure ("+absDefault.getAbsType()+") not compatible with template ("+absTemplate.getAbsType()+")");
		}
		
		// Loop all slots
		for (String slotName : absTemplate.getNames()) {
			AbsObject slotTemplateValue = absTemplate.getAbsObject(slotName);
			AbsObject absDefaultValue = absDefault.getAbsObject(slotName);
			if (absDefaultValue != null) {
				AbsHelper.setAttribute(absTemplate, slotName, applyDefaultValues(slotTemplateValue, absDefaultValue));
			}
		}
		return absTemplate;
	}
	
	/**
	 * Generate an AbsObject consistently with class. 
	 * @param clazz class to convert
	 * @param onto reference ontology
	 * @return abs-object
	 * @throws OntologyException
	 */
	public static AbsObject createAbsTemplate(Class clazz, Ontology onto) throws OntologyException {
		// Convert class into schema
		// Try to get associated schema from ontology
		ObjectSchema schema = onto.getSchema(clazz);
		
		// If no schema found, try as an aggregate 
		if (schema == null) {
			schema = AggregateHelper.getSchema(clazz, null);
		}
		
		return createAbsTemplate(schema, null, VarIndexWrapper.ZERO);
	}
	
	/**
	 * Generate an AbsObject consistently with schema. 
	 * @param schema to convert
	 * @return abs-object
	 * @throws Exception
	 */
	public static AbsObject createAbsTemplate(ObjectSchema schema) throws OntologyException {
		return createAbsTemplate(schema, null, VarIndexWrapper.ZERO);
	}
	
	/**
	 * Generate an AbsObject consistently with schema.
	 * All variables are prefixed 
	 * @param schema to convert
	 * @param prefix for variable
	 * @return abs-object
	 * @throws Exception
	 */
	public static AbsObject createAbsTemplate(ObjectSchema schema, String prefix) throws OntologyException {
		return createAbsTemplate(schema, prefix, VarIndexWrapper.ZERO);
	}

	private static AbsObject createAbsTemplate(ObjectSchema schema, String prefix, VarIndexWrapper viw) throws OntologyException {
		
		// For primitive schemas and TermSchema (bean object type) 
		if (schema instanceof PrimitiveSchema || schema.getClass() == TermSchema.class) {
			return new AbsVariable(createVariableName(prefix, viw), schema.getTypeName());
		}

		if (schema instanceof AggregateSchema) {
			AggregateSchema aggregateSchema = (AggregateSchema)schema; 
			AbsAggregate aggregate = new AbsAggregate(aggregateSchema.getTypeName());
			
			// If is present the element schema add this information in aggregate
			ObjectSchema elementsSchema = aggregateSchema.getElementsSchema();
			if (elementsSchema != null) {
				aggregate.setElementTemplate((AbsTerm)createAbsTemplate(elementsSchema, prefix, viw));
			}
			return aggregate;
		}
		
		AbsObject abs = schema.newInstance();
		for (String slotName : schema.getNames()) {
			ObjectSchema slotSchema = schema.getSchema(slotName);
			setAttribute(abs, slotName, createAbsTemplate(slotSchema, prefix, viw));
		}
		return abs;
	}

	private static String createVariableName(final String prefix, VarIndexWrapper viw) {
		String varName = (prefix!=null ? prefix : "") + "v#" + viw.index;
		viw.index++;
		return varName;
	}

	
	/**
	 * Inner class used to manage variable name generation 
	 */
	static class VarIndexWrapper {
		static VarIndexWrapper ZERO = new VarIndexWrapper(0); 
		int index;
		public VarIndexWrapper(int index) {
			this.index = index;
		}
	}
	//#J2ME_EXCLUDE_END
}


