package com.openMap1.mapper.mapping;
import com.openMap1.mapper.core.Xpth;
import com.openMap1.mapper.core.namespace;
import com.openMap1.mapper.core.NamespaceSet;
import com.openMap1.mapper.core.ClassSet;
import com.openMap1.mapper.core.MapperException;
import com.openMap1.mapper.core.XpthException;
import com.openMap1.mapper.util.GenUtil;
import com.openMap1.mapper.util.ModelUtil;
import com.openMap1.mapper.util.messageChannel;
import com.openMap1.mapper.writer.XSLGenerator;
import com.openMap1.mapper.writer.whenValue;
import com.openMap1.mapper.writer.outputContext;
import com.openMap1.mapper.AssocEndMapping;
import com.openMap1.mapper.AttributeDef;
import com.openMap1.mapper.CrossCondition;
import com.openMap1.mapper.MappedStructure;
import com.openMap1.mapper.Mapping;
import com.openMap1.mapper.MappingCondition;
import com.openMap1.mapper.NodeDef;
import com.openMap1.mapper.PropMapping;
import com.openMap1.mapper.ValueCondition;
import java.util.Enumeration;
import java.util.Vector;
import java.util.Iterator;
/** superclass for all mappings between the XML and the class model
* - mappings representing objects, properties and associations.
* Now a wrapper class for the Mapper model class Mapping */
public class MappingTwo
{
public static int OBJECT = 0;
public static int PROPERTY = 1;
public static int ASSOCIATION = 2;
public String mappingTypeString() {return mappingTypeString[mappingType];}
private String[] mappingTypeString = {"object","property","assoc"};
public int mappingType() {return mappingType;}
protected int mappingType;
private Mapping map;
/** the model mapping object which this is a wrapper for */
public Mapping map() {return map;}
private messageChannel mChan;
public messageChannel mChan() {return mChan;}
/** XPath from the root to the node representing something */
public Xpth nodePath()
{
Xpth np = null;
try{np = map.getRootXPath();}
catch (MapperException ex){GenUtil.surprise(ex,"mapper.nodePath");}
return np;
}
public NodeDef mappedNode()
{
MappedStructure ms = ModelUtil.getMappedStructure(map);
return ms.getNodeDefByPath(nodePath().stringForm());
}
/** qualified class name of the object mapped, or object owning the property mapped */
public String className() {return map.getQualifiedClassName();}
/** subset of the object mapped, or object owning the property mapped.
* default ""; otherwise denotes that this node is one of several nodes representing objects,
* properties or associationEnds of this class.
*/
public String subset() {return map.getSubset();}
/**
* @return true if the mapping has a subset other than null or ""
*/
public boolean hasSubset() {
if (subset() == null) return false;
if (subset().equals("")) return false;
return true;
}
/** class and subset of the object mapped, or object owning the property mapped */
public ClassSet cSet() {return map.getClassSet();}
/**
* @return level in the stack of XOReaders from which this mapping came,
* for XSLT generation
*/
public int stackLevel() {return stackLevel;}
private int stackLevel = 0;
/**
* set the level in the stack of XOReaders from which this mapping came,
* for XSLT generation
* @param level
*/
public void setStackLevel(int level) {stackLevel = level;}
/**
* class and subset of the object mapped, or object owning the property mapped.
* The subset has a prefix which defines the XOReader stack level, for XSLT generation
* */
public ClassSet XSLCSet()
{
ClassSet cSet = null;
try {
String XSLSubset = "m" + stackLevel + "_" + subset();
cSet = new ClassSet(className(), XSLSubset);
}
catch (Exception ex)
{
System.out.println("Exception making XSL ClassSet ");
System.out.println(className() + " " + subset());
}
return cSet;
}
/* the mapping only represents what it represents when all these conditions are true. */
private Vector<whenCondition> whenConditions = new Vector<whenCondition>();
private Vector<linkCondition> linkConditions = new Vector<linkCondition>(); // vector of link conditions
/** Vector of when-conditions which must hold for the mapping to apply */
public Vector<whenCondition> whenConditions() {return whenConditions;}
/** Vector of link conditions which must hold for the mapping to apply */
public Vector<linkCondition> linkConditions() {return linkConditions;}
/** Vector of all link and when-conditions which must hold for the mapping to apply */
public Vector<Condition> allConditions()
{
Vector<Condition> allConditions = new Vector<Condition>();
for (Iterator<linkCondition> il = linkConditions.iterator();il.hasNext();)
allConditions.add(il.next());
for (Iterator<whenCondition> iw = whenConditions.iterator();iw.hasNext();)
allConditions.add(iw.next());
return allConditions;
}
/** multiway describes choice or redundant mappings */
public String multiWay() {return map.getMultiWay().getLiteral();}
/** One of the when-conditions which must hold for the mapping to apply */
public whenCondition whenCondition(int i) {return whenConditions.elementAt(i);}
/**
* One of the link conditions which must hold for the mapping to apply
*
* @param i int index of the condition
* @return linkCondition the link condition
*/
public linkCondition linkCondition(int i) {return linkConditions.elementAt(i);}
/** Add a when-condition which must hold for the mapping to apply */
public void addWhenCondition(whenCondition wc) {whenConditions.addElement(wc);}
/** Add a link condition which must hold for the mapping to apply */
public void addLinkCondition(linkCondition lc) {linkConditions.addElement(lc);}
/** true if this is a mapping to an XML attribute (not an element) */
public boolean isXMLAttributeMapping() {return (map.mappedNode() instanceof AttributeDef);}
//---------------------------------------------------------------------------------------------------------
// constructor
//---------------------------------------------------------------------------------------------------------
/**
*
* @param map
*/
public MappingTwo(Mapping map, messageChannel mChan) throws MapperException
{
this.map = map;
this.mChan = mChan;
for (Iterator<MappingCondition> it = map.getMappingConditions().iterator();it.hasNext();)
{
MappingCondition mc = it.next();
if (mc instanceof ValueCondition)
{
addWhenCondition(new whenCondition((ValueCondition)mc));
}
else if (mc instanceof CrossCondition) try
{
int end = 0;
if (map instanceof AssocEndMapping)
{end = ((AssocEndMapping)map).getEnd();}
else if (map instanceof PropMapping)
{end = 0;} // allowed case; no change
else throw new MapperException("Cross condition on a mapping which is not "
+ " a property mapping or an association end mapping");
addLinkCondition(new linkCondition((CrossCondition)mc, end));
}
/* sometimes an object mapping required in a link condition is missing,
* and I do not want this to throw the whole MDLBase initialisation.*/
catch (MapperException ex) {System.out.println(ex.getMessage());}
}
}
//---------------------------------------------------------------------------------------------------------
// namespaces
//---------------------------------------------------------------------------------------------------------
private namespace MDLNamespace = GenUtil.defaultMDLNamespace();
public namespace MDLNamespace() {return MDLNamespace;}
String MDLPrefix() {return MDLNamespace().prefix();}
String MDLURI() {return MDLNamespace().URI();}
/**
* reset the URI of the MDL namespace
* @param newURI String the new URI (should identify the class model being mapped onto)
*/
public void setMDLURI(String newURI) {MDLNamespace = new namespace(GenUtil.defaultMDLNamespace().prefix(),newURI);}
/** full namespace set, including the MDL namespace and the namespaces of the mapped XML */
public NamespaceSet NSSet()
{
NamespaceSet mappedSpaces = null;
try
{
mappedSpaces = ModelUtil.getGlobalNamespaceSet(map);
mappedSpaces.addNamespace(MDLNamespace);
}
catch (MapperException ex) {GenUtil.surprise(ex, "mapper.NSSet");}
return mappedSpaces;
}
//---------------------------------------------------------------------------------------------------------
// other stuff
//---------------------------------------------------------------------------------------------------------
/** should always be overridden, but just in case... */
public void write(messageChannel mChan)
{
mChan.message("");
mChan.message("Mapping to node " + nodePath().stringForm());
writeConditions();
}
/** class and subset of the mapping */
public ClassSet getClassSet() {return cSet();}
/** name of the representing node ( = prefixed element name or attribute name) */
String nodeName() {return map.mappedNode().getName();}
/** XPaths to nodes required to evaluate 'when' conditions for this representation */
public Vector<Xpth> whenConditionPaths()
{
int i;
Vector<Xpth> res = new Vector<Xpth>();
for (i = 0; i < whenConditions.size(); i++)
{
whenCondition wc = whenConditions.elementAt(i);
// do not include when conditions that apply a function to the LHS
if (wc.getLeftFunction().equals(""))
res.addElement(wc.rootToLeftValue());
}
return res;
}
/** XPaths to nodes required to evaluate 'when' and link conditions for this representation */
public Vector<Xpth> allConditionPaths()
{
Vector<Xpth> allC = new Vector<Xpth>();
for (Iterator<Xpth> it = whenConditionPaths().iterator();it.hasNext();) {allC.add(it.next());}
for (Iterator<Xpth> it = linkConditionPaths().iterator();it.hasNext();) {allC.add(it.next());}
return allC;
}
/** true if the 'when' conditions of this mapping are mutually exclusive with the 'when'
* conditions of some other mapping - i.e if the two mappings cannot apply to
* the same node instance.*/
public boolean exclusiveWhenConditions(MappingTwo m) throws XpthException
{
int i,j;
whenCondition w1,w2;
boolean res = false;
if (nodePath().equalPath(m.nodePath())) for (i = 0; i < whenConditions.size(); i++)
{
w1 = whenConditions.elementAt(i);
for (j = 0; j < m.whenConditions.size(); j++)
{
w2 = m.whenConditions.elementAt(j);
if (w1.mutualExclusive(w2)) res = true;
}
}
return res;
}
/** to be overridden */
public String description()
{
return("generic mapping");
}
/** XPath form of when-conditions */
public String XPathWhenTests(XSLGenerator XX) throws MapperException
{
String res = "";
if (whenConditions.size() > 0)
{
res = "[";
for (int i = 0; i < whenConditions.size(); i++)
{
if (i > 0) res = res + " and ";
whenCondition wc = whenConditions.elementAt(i);
res = res + wc.XPathForm(XX);
}
res = res + "]";
}
return res;
}
/** XPath form of link conditions */
public String XPathLinkTests(XSLGenerator XX,boolean fromObject,String startVar) throws XpthException
{
String res = "";
if (linkConditions.size() > 0)
{
res = "[";
for (int i = 0; i < linkConditions.size(); i++)
{
if (i > 0) res = res + " and ";
linkCondition lc = linkConditions.elementAt(i);
res = res + lc.XPathTest(XX,fromObject,startVar);
}
res = res + "]";
}
return res;
}
/** XPaths to nodes required to evaluate link conditions for this representation
* FIXME - this is obviously wrong. */
public Vector<Xpth> linkConditionPaths()
{return new Vector<Xpth>();}
/** write out text form of when-conditions and link conditions */
public void writeConditions()
{
for (int i = 0; i < whenConditions.size(); i++)
{
whenCondition wc = whenCondition(i);
mChan.message("When: {" + wc.lhsEndToLeftValue().stringForm() + "} " + wc.test() + " '" + wc.rightValue() + "'");
}
for (int i = 0; i < linkConditions.size(); i++)
{
linkCondition lc = linkCondition(i);
mChan.message("Link: {" + lc.lhsEndToLeftValue().stringForm() + "} " + lc.test() + " {" + lc.rhsEndToRightValue().stringForm() + "}");
}
}
/** maximum absolute path length to this mapping, or to any of its when-condition or link
* condition values */
public int mappingDepth() throws MapperException
{
int depth = 0;
if (nodePath().definite())
{
depth = nodePath().size();
for (int i = 0; i < whenConditionPaths().size(); i++)
{
Xpth wp = (Xpth)whenConditionPaths().elementAt(i);
if (wp.size() > depth) depth = wp.size();
}
for (int i = 0; i < linkConditions().size(); i++)
{
linkCondition lc = linkConditions().elementAt(i);
if (lc.rootToLeftValue.size() > depth) depth = lc.rootToLeftValue.size();
if (lc.rootToRightValue().size() > depth) depth = lc.rootToRightValue().size();
}
}
return depth;
}
/** maximum inner path length inside the '//' step to this mapping,
* or to any of its when-condition or link condition values */
public int innerDepth() throws XpthException
{
int depth = 0;
if (!nodePath().definite())
{
depth = nodePath().innerSize();
for (int i = 0; i < whenConditionPaths().size(); i++)
{
Xpth wp = (Xpth)whenConditionPaths().elementAt(i);
if (wp.innerSize() > depth) depth = wp.innerSize();
}
for (int i = 0; i < linkConditions().size(); i++)
{
linkCondition lc = linkConditions().elementAt(i);
if (lc.rootToLeftValue.innerSize() > depth) depth = lc.rootToLeftValue.innerSize();
if (lc.rootToRightValue().innerSize() > depth) depth = lc.rootToRightValue().innerSize();
}
}
return depth;
}
/** true if this mapping is applicable in a given subtree context.
* The context defines a set of when-condition values, and the nodes that represent them.
* If any values conflict with the value of a when-condition defined on the same node in this mapping,
* this mapping is not applicable.
* Assumes that the only tests applied in when-conditions are '='. */
public boolean applicable(outputContext oc) throws XpthException
{
int i;
boolean applicable = true;
whenValue wv;
whenCondition wc;
for (i = 0; i < whenConditions.size(); i++)
{
wc = whenConditions.elementAt(i);
for (Enumeration<whenValue> en = oc.whenValues().elements(); en.hasMoreElements();)
{
wv = en.nextElement();
if ((wc.rootToLeftValue().compatible(wv.rootPath())) &&
(!(wv.value().equals(wc.rightValue())))) applicable = false;
}
}
return applicable;
}
public static Vector<MappingTwo> vCopy(Vector<MappingTwo> v)
{
Vector<MappingTwo> res = new Vector<MappingTwo>();
for (Iterator<MappingTwo> it = v.iterator();it.hasNext();) res.add(it.next());
return res;
}
}