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);} }