package com.openMap1.mapper.writer;
import com.openMap1.mapper.mapping.*;
import com.openMap1.mapper.core.*;
import com.openMap1.mapper.reader.*;
import com.openMap1.mapper.util.*;
import java.util.*;
/* 24/2/02 made some rather hasty changes to this class to make it compatible with other changes - eg
multiple representations per subset. Need checking that they make sense.
*/
/**
* Implementation of the objectCapability interface, using a set of mappings
*/
public class XOCapability implements objectCapability
{
private MDLXOReader XR;
public XOCapability(MDLXOReader xr)
{
XR = xr;
}
/**
* return the URI of the object model which this XOReader implements
*/
public String objectModelURIX() throws MDLReadException
{
String res = XR.mdlSpace().URI();
if (res == null) throw new MDLReadException("The XML Reader has no URI of an object model.");
return res;
}
/* whether or not the source represents the class at all;
true if it represents any subset or subclass.*/
public boolean representsClassX(String className)
{
int i;
Vector<String> cNames;
String cName;
boolean res = false;
cNames = XR.allClasses();
/* if any inheritor class of the class is represented...*/
for (i = 0; i < cNames.size(); i++)
{
cName = (String)cNames.elementAt(i);
if (GenUtil.inVector(cName,XR.inheritors(className))) res = true;
}
return res;
}
/* true if the source recognises that class subClass inherits
from class superClass (and so e.g. retrieves objects of the subClass if
you ask for objects of the superClass. */
public boolean inheritsFromX(String subClass,String superClass)
{
boolean res = false;
Vector<String> subs = XR.inheritors(superClass);
if (subs != null)
{res = GenUtil.inVector(subClass,subs);}
return res;
}
/* whether each object in the class is represented uniquely
(if false, the source may return several versions of the same object).
True only if objects are uniquely represented for all represented subsets
and subclasses. */
public boolean uniqueObjectX(String className)
{
int i;
boolean res = true;
ClassSet cs;
objectMapping om;
Vector<ClassSet> scs = XR.subClassSets(className);
for (i = 0; i < scs.size(); i++)
{
cs = (ClassSet)scs.elementAt(i);
om = XR.namedObjectMapping(cs);
/* if any subclass or subset is represented non-uniquely,
you cannot guarantee unique representation. */
if ((om != null) && (!om.isUnique())) res = false;
}
return res;
}
/* a Vector of filter objects (filterAssoc or filterProperty)
for all inclusion filters. If the source represents several subsets
of the class, or subclasses, only those filters common to all subsets
and subclasses are returned. */
public Vector<filter> inclusionFiltersX(String className)
{
int i,j;
filter f;
objectMapping om;
Vector<filter> res,temp;
ClassSet cs;
res = null;
Vector<ClassSet> scs = XR.subClassSets(className);
for (i = 0; i < scs.size(); i++)
{
cs = (ClassSet)scs.elementAt(i);
om = XR.namedObjectMapping(cs);
if (res == null)
{res = om.inclusionFilters();}
else if (om != null)
{
temp = res;
res = new Vector<filter>();
for (j = 0; j < temp.size(); j++)
{
f = temp.elementAt(j);
if (f.inFilters(om.inclusionFilters())) res.addElement(f);
}
}
}
return res;
}
/* whether the source represents the property.
If it represents subsets of the class or subclasses,
returns true if the property is represented for any subsets and subclasses
i.e. if it can ever be returned.
Not true for converted properties.
True for any property not directly represented, but given by a format
conversion from represented converted properties.
*/
public boolean representsPropertyX(String className, String propertyName)
{
ClassSet cs;
boolean res= false;
Vector<ClassSet> scs = XR.subClassSets(className);
for (int i = 0; i < scs.size(); i++)
{
cs = (ClassSet)scs.elementAt(i);
if (XR.allTruePropertyReps(cs).get(propertyName) != null) res = true;
}
return res;
}
/* whether the source represents the property as optional, i.e. sometimes absent.
True if it is optional or not represented for any subset or subclass.
False only if the property is represented and non-optional for all subsets
and subclasses, i.e. can be relied upon to be present. */
public boolean optionalPropertyX(String className, String propertyName) throws MapperException
{
boolean res= false; // start out assuming non-optional
if (!representsPropertyX(className,propertyName))
{throw new MDLReadException("Property '" + propertyName + "' of class '"
+ className + "' cannot be optional or non-optional, as it is not represented.");}
Vector<ClassSet> scs = XR.subClassSets(className);
for (int i = 0; i < scs.size(); i++)
{
ClassSet cs = (ClassSet)scs.elementAt(i);
if (classSetOptionalProperty(cs, propertyName)) res = true;
}
return res;
}
private boolean classSetOptionalProperty(ClassSet cs, String propertyName)
throws MapperException
{
boolean res = false;
objectMapping om = XR.namedObjectMapping(cs);
Vector<propertyMapping> pms = XR.namedPropertyMappings(cs.className(),cs.subset(),propertyName);
propertyConversion pc = XR.getInConversion(cs,propertyName);
if (pc != null)
{
// a property got by conversion is optional if any of its arguments are optional
for (int i = 0; i < pc.arguments().size(); i++)
{
String arg = (String)pc.arguments().elementAt(i);
if (classSetOptionalProperty(cs,arg)) res = true;
}
}
// if property is missing from some subset, maker it optional overall
else if (pms.size() == 0)
{res = true;}
// if any mapping for the subset is optional, make the property optional
else for (int i = 0; i < pms.size();i++)
{
propertyMapping pm = (propertyMapping)pms.elementAt(i);
if (!pm.fixed() && // if it is fixed, it cannot be optional
!(pm.optionalFromMDL().equals("no")) && // if the MDL says it is not optional, this overrides XML structure
!(om.mappedNode().nonOptionalPath(pm.objectToProperty()))) // deduce from XML structure
{res = true;}
}
return res;
}
/**
* If the object source is aware of any combination of properties
which constitute a unique identifier for objects of the class, and if it
is capable of returning those properties (and if they are all
non-optional when mustBeNonOptional = true), return a Vector of the property names.
Otherwise return null. */
public Vector<String> primaryKeyX(String className, boolean mustBeNonOptional)
throws MapperException
{
Vector<String> pKey = null;
Vector<Vector<String>> candidates = XR.uniqueIdentifiers(className);
if (candidates != null)
{
boolean found = false;
for (int i = 0; i < candidates.size(); i++) if (!found)
{
//message("candidate key " + i + " for class " + className);
Vector<String> candidateKey = candidates.elementAt(i);
boolean OK = true;
for (int j = 0; j < candidateKey.size(); j++)
{
String field = (String)candidateKey.elementAt(j);
if (!representsPropertyX(className,field)) OK = false;
if (OK && mustBeNonOptional && optionalPropertyX(className,field)) OK = false;
}
if (OK)
{
found = true;
pKey = candidateKey;
}
}
}
return pKey;
}
/**
* a Vector of all properties of the class represented in the mappings,
* which are not fixed and do not have defaults
* - used as a primary key in XSLT generation if no primary key has been defined
* @param className String
* @return Vector the property names
*/
public Vector<String> allRepresentedVariableProperties(String className)
{
Vector<String> mProps = new Vector<String>();
Hashtable<String, String> mappedProps = new Hashtable<String, String>();
for (int c = 0; c < XR.propertyMappings().size(); c++)
{
propertyMapping pm = (propertyMapping)XR.propertyMappings().elementAt(c);
// do not use properties which are fixed or have defaults as part of a 'primary key'
if ((pm.className().equals(className)) && (!pm.fixed()) && (!pm.hasDefault()))
{
mappedProps.put(pm.propertyName(), "1");
}
}
for (Enumeration<String> en = mappedProps.keys(); en.hasMoreElements();)
{
String pName = en.nextElement();
mProps.addElement(pName);
}
return mProps;
}
/* whether the source represents the association.
If it represents subsets of either class or subclasses,
returns true if the association is represented for any subsets and subclasses
of the objects at both ends - i.e. if associated objects can ever be returned. */
public boolean representsAssociationX(String class1, String assocName, String class2)
{
boolean res = false;
Vector<ClassSet> scs1 = XR.subClassSets(class1);
Vector<ClassSet> scs2 = XR.subClassSets(class2);
for (int i=0; i < scs1.size(); i++)
{
ClassSet cs1 = (ClassSet)scs1.elementAt(i);
for (int j=0; j < scs2.size(); j++)
{
ClassSet cs2 = (ClassSet)scs2.elementAt(j);
if (XR.namedAssociationMappings(cs1, assocName, cs2).size() > 0)
res = true;
}
}
return res;
}
/* end = 1 or 2 for class1 or class2 in the association.
returns an array s with s[0] = minCardinality for the end ,
s[1] = maxCardinality for the end.
Allowed values are "0", "1", "N" and "N1" (may be 1 or N;not known).
"N1" occurs when the XPaths allow a maxCardinality "N", but there are link
conditions which may reduce the maxCardinality to 1.
Min Cardinality = 1 only if it is 1 for all subsets and subclasses,
i.e. if the object can be relied upon to be present.
Max Cardinality= 1 only if it is 1 for all subsets and subclasses
at both ends of the association - if at most one associated object can ever
be returned.
When the association links two subsets of one class A to one subset of another class B,
then individual representations may have maxCardinality "1" at the 'A' end,
but overall the maxCardinality is "N".
If any subclass/subset at one end is non-uniquely represented, but has some
represented unique identifier properties, then the maxCardinality at the other end
is "N" - because there may be several nodes representing one object at the non-unique end.
*/
public String[] cardinalityX(String class1, String assocName, String class2, int end)
throws MapperException
{
String[] res = {"undefined","undefined"};
int i,j,k,assocs;
Vector<ClassSet> startSubsets,endSubsets;
ClassSet startCS,endCS;
Vector<AssociationMapping> ams;
objectMapping om; // for start object
AssociationMapping am;
associationEndMapping aes,aee;
boolean allMinIs1, allMaxIs1; // true if overall min cardinality = 1 and max cardinality =1 respectively
boolean minStart,maxStart; // as above for one subclass and subset of the start object
boolean minIs1,maxIs1; // as above for one subclass and subset of start and end objects
String[] classes = new String[2]; // names of classes at end 1,2
ClassSet[] subCSet = new ClassSet[2]; // (subclass,subset) at end 1,2
//message("Finding cardinality for [" + class1 + "]" + assocName + "[" + class2 + "], end " + end);
if ((end ==1)|(end ==2))
{
allMinIs1 = true; // if this remains true for all start subclasses and subsets, min cardinality = 1
allMaxIs1 = true; // if this remains true for all start subclasses and subsets, max cardinality = 1
classes[0] = class1;
classes[1] = class2;
endSubsets = XR.subClassSets(classes[end-1]); // e.g. subClassSets(class2) if end = 2
startSubsets = XR.subClassSets(classes[2-end]); // e.g. subClassSets(class1) if end = 2
// outer loop, for subclasses and subsets at the start end of the association
for (i = 0; i < startSubsets.size(); i++)
{
startCS = (ClassSet)startSubsets.elementAt(i);
om = XR.namedObjectMapping(startCS); // object mapping for start object
subCSet[2-end]=startCS; // e.g. subCSet[0] if end = 2
minStart = false; // becomes true if min Cardinality = 1 for this start subclass and subset
maxStart = true; // remains true if max Cardinality = 1 for this start subclass and subset
assocs = 0; // number of association mappings for one start subclass and subset.
/* inner loop for subclasses and subsets at the final end of the association,
where represented cardinality is being found. */
for (j = 0; j < endSubsets.size(); j++)
{
endCS = (ClassSet)endSubsets.elementAt(j);
subCSet[end-1]=endCS; // e.g. subCSet[1] if end = 2
ams = XR.namedAssociationMappings(subCSet[0], assocName, subCSet[1]);
if (ams.size() > 0)
{
assocs++;
for (k = 0; k < ams.size(); k++)
{
am = (AssociationMapping)ams.elementAt(k);
//message("found association mapping");
aes = am.assocEnd(2-end); // start end of association mapping
aee = am.assocEnd(end-1); // finish end of association mapping
// true if represented min cardinality is 1
minIs1 = (om.mappedNode().nonOptionalPath(aes.objToAssoc())) &&
(om.mappedNode().nonOptionalPath(aee.assocToObj())) &&
(aes.linkConditions().size()==0) &&
(aee.linkConditions().size()==0);
// minCardinality 1 from MDL overrides minCardinality from XML structure
if (aee.minCardinalityFromMDL().equals("1")) minIs1 = true;
// true if represented max cardinality is 1
maxIs1 = om.mappedNode().uniquePath(aes.objToAssoc()) &&
(am.mappedNode().uniquePath(aee.assocToObj())|
(aee.uniqueLinkConditions()));
// maxCardinality 1 from MDL overrides maxCardinality from XML structure
if (aee.maxCardinalityFromMDL().equals("1")) maxIs1 = true;
// if the start object is not uniquely represented, maxCardinality at the other end is N
if (!om.isUnique()) maxIs1 = false;
/* if any end subclass and subset guarantees an end object,
then one is guaranteed for this start subclass and subset. */
if (minIs1) minStart = true;
/* if any end subset can give more than one end object,
then more than one can occur for this start subclass and subset. */
if (!maxIs1) maxStart = false;
}
}
} // end of loop over end subclasses and subsets
// if there is an association to more than one end class subset, the association is multiple
if (assocs > 1) maxStart = false;
// if for any start subset the association is optional, it is optional overall
if (!minStart) allMinIs1 = false;
// if for any start subset the association is multiple, it is multiple overall
if (!maxStart) allMaxIs1 = false;
} // end of loop over start subclasses and subsets
if (allMinIs1) {res[0] = "1";} else {res[0] = "0";}
if (allMaxIs1) {res[1] = "1";} else {res[1] = "N";}
}
else {throw new MapperException("Association end '" + end + "' is not allowed.");}
return res;
// phew!
}
void message(String s) {System.out.println(s);}
}