package com.openMap1.mapper.writer; import com.openMap1.mapper.mapping.*; import com.openMap1.mapper.core.*; import com.openMap1.mapper.util.messageChannel; import java.util.*; //------------------------------------------------------------------------------------------ // class for sets of representations on a unique subtree (US) //------------------------------------------------------------------------------------------ /** * class for sets of representations on a unique subtree (US) * * @author Robert Worden * @version 1.0 */ public class USRepSet extends repSet { private Hashtable<String, Vector<String>> subtreeWhenVals; /** subTreeWhenVals has key = root path string form of a node in the unique subtree, * and value = the whenVals Vector for the node. * <p> * Only includes those when-condition nodes whose values were not fixed * at any higher node, i.e nodes which are not in the unique subtree of a higher node. */ public Hashtable<String, Vector<String>> subtreeWhenVals() {return subtreeWhenVals;} private TreeElement subTree; /** The unique subtree - those nodes below this one with mincardinality = maxCardinality = 1 */ public TreeElement subTree() {return subTree;} private int minUndefinedSteps; /** minimum number of steps from the root in the actual root path which * are left undefined in all mappings to any node in the unique subtree.*/ public int minUndefinedSteps() {return minUndefinedSteps;} private int maxOuterDefiniteSteps = 0; /** the maximum number of definite steps from the root to any '//' step for any mapping in the set. */ public int maxOuterDefiniteSteps() {return maxOuterDefiniteSteps;} private boolean hasDefinitePaths = false; /** whether any of the mappings are to a definite XPath */ public boolean hasDefinitePaths() {return hasDefinitePaths;} private boolean hasIndefinitePaths = false; /** whether any of the mappings are to an indefinite XPath */ public boolean hasIndefinitePaths() {return hasIndefinitePaths;} private Vector<nodeRepSet> nodeRepSets = new Vector<nodeRepSet>(); /** Vector of all the individual nodeRepsets that make up this USRepSet */ public Vector<nodeRepSet> nodeRepSets() {return nodeRepSets;} //--------------------------------------------------------------------------- // constructors //--------------------------------------------------------------------------- /** * constructor for a repSet for a unique subtree, * including only mappings applicable in the subtree context. * * @param os StructureDefinition output XML tree structure definition * @param md MDLBase mappings to output XML * @param rp Xpth path to this node * @param rootTree treeElement * @param oc outputContext include only mappings applicable in the subtree context. * @throws MDLReadException */ public USRepSet(MDLBase md, Xpth rp, TreeElement rootTree, outputContext oc) throws MapperException { super(md,rp); nodeRepSet thisNodeRepSet = new nodeRepSet(MD,rp,oc); nodeRepSets.addElement(thisNodeRepSet); appendMaps(thisNodeRepSet); hasDefinitePaths = thisNodeRepSet.hasDefinitePaths(); hasIndefinitePaths = thisNodeRepSet.hasIndefinitePaths(); minUndefinedSteps = thisNodeRepSet.minUndefinedSteps(); maxOuterDefiniteSteps = thisNodeRepSet.maxOuterDefiniteSteps(); subtreeWhenVals = new Hashtable<String, Vector<String>>(); // do not add when-condition value vectors for values already fixed at a higher node if ((!(thisNodeRepSet.alreadyFixed())) && (thisNodeRepSet.whenVals().size() > 0)) {subtreeWhenVals.put(rp.stringForm(),thisNodeRepSet.whenVals());} TreeElement uSub =rootTree.fromRootPath(rp,true).uniqueSubtree(); //message("Unique subtree size: " + uSub.size()); subTree = uSub; for (int i = 0; i < uSub.childTreeElements().size(); i++) { TreeElement child = (TreeElement)uSub.childTreeElements().elementAt(i); Xpth rpp = rp.addInnerStep(child.tagName()); addRepSetInfo(subtreeWhenVals,rpp,rootTree,oc); } for (int i = 0; i < uSub.attributes().size(); i++) { Xpth rpp = rp.addInnerStep("@" + uSub.attribute(i)); nodeRepSet attReps = new nodeRepSet(MD,rpp,oc); appendMaps(attReps); if (attReps.hasDefinitePaths()) hasDefinitePaths = true; if (attReps.hasIndefinitePaths()) hasIndefinitePaths = true; nodePaths().addElement(rpp); nodeRepSets.addElement(attReps); // do not add when-condition value vectors for values already fixed at a higher node if ((!(attReps.alreadyFixed())) && (attReps.whenVals().size() > 0)) {subtreeWhenVals.put(rpp.stringForm(),attReps.whenVals());} } } /** find the repSet for a unique child node, and add all its information to this repSet. */ void addRepSetInfo(Hashtable<String, Vector<String>> subtreeWhenVals, Xpth rpp, TreeElement rootTree, outputContext oc) throws MapperException { USRepSet cRep = new USRepSet(MD,rpp,rootTree,oc); appendMaps(cRep); nodeRepSets = addNodeRepSets(nodeRepSets,cRep.nodeRepSets()); if (cRep.hasDefinitePaths()) hasDefinitePaths = true; if (cRep.hasIndefinitePaths()) hasIndefinitePaths = true; if (cRep.minUndefinedSteps() < minUndefinedSteps) {minUndefinedSteps = cRep.minUndefinedSteps();} if (cRep.maxOuterDefiniteSteps() > maxOuterDefiniteSteps) {maxOuterDefiniteSteps = cRep.maxOuterDefiniteSteps();} addXpths(nodePaths(),cRep.nodePaths()); for (Enumeration<String> en = cRep.subtreeWhenVals().keys(); en.hasMoreElements();) { String rpn = en.nextElement(); Vector<String> values = cRep.subtreeWhenVals().get(rpn); subtreeWhenVals.put(rpn,values); } } /** * constructor for a copy of an existing USRepSet, including only * mappings dependent on one primary object mapping. * * @param usr USRepSet * @param primary objectMapping */ public USRepSet(USRepSet usr, objectMapping primary) throws MapperException { //set up empty Vectors of mappings super(usr.MD,usr.rootPath); // copy all trivia nodePaths = usr.nodePaths(); subtreeWhenVals = usr.subtreeWhenVals(); subTree = usr.subTree(); minUndefinedSteps = usr.minUndefinedSteps(); maxOuterDefiniteSteps = usr.maxOuterDefiniteSteps(); hasDefinitePaths = usr.hasDefinitePaths(); hasIndefinitePaths = usr.hasIndefinitePaths(); nodeRepSets = usr.nodeRepSets(); // copy across all mappings with condition values in the subtree whenMaps = usr.whenMaps(); linkMaps = usr.linkMaps(); // copy from usr all mappings dependent on one primary mapping copyDependentMappings(usr,primary); } // copy from another USRepSet usr all mappings dependent on one primary mapping private void copyDependentMappings(USRepSet usr, objectMapping primary) throws MapperException { ClassSet pcs = primary.cSet(); // copy the primary object mapping addObjectMap(primary); //copy all property mappings for the primary ClassSet for (int i = 0; i < usr.propertyMaps().size(); i++) { propertyMapping pm = (propertyMapping)usr.propertyMaps().elementAt(i); if (pm.cSet().equals(pcs)) addPropertyMap(pm); } /* copy the association mappings that the primary ClassSet is dependent on, recursively find all classes dependent on the primary class through associations, and copy their mappings */ for (int i = 0; i < usr.assocMaps().size(); i++) { AssociationMapping am = (AssociationMapping)usr.assocMaps().elementAt(i); for (int thisEnd = 0; thisEnd < 2; thisEnd++) { associationEndMapping thisMap = am.assocEnd(thisEnd); associationEndMapping thatMap = am.assocEnd(1 -thisEnd); if (pcs.equals(thisMap.cSet())) { // add the association mapping that this object depends on if (thisMap.required()) {addAssocMap(am);} // find other classes dependent on this class through associations else if ((!thisMap.required()) && (thatMap.required())) { for (int tc = 0; tc < usr.objectMaps().size(); tc++) { objectMapping om = (objectMapping)usr.objectMaps().elementAt(tc); if (thatMap.cSet().equals(om.cSet())) copyDependentMappings(usr,om); } } } } } } //--------------------------------------------------------------------------- // iterating over possible combinations of when-condition values //--------------------------------------------------------------------------- /** the total number of combinations of when-condition values, * for a unique-subtree repSet (not a single-node repSet). */ public int whenCombinations() { int res = 1; for (Enumeration<Vector<String>> en = subtreeWhenVals.elements(); en.hasMoreElements();) { Vector<String> v = en.nextElement(); res = res*v.size(); } return res; } /** return a Vector of whenValue objects for one particular allowed combination * of when-condition values. */ public Vector<whenValue> whenCombination(int index) throws MapperException { Vector<whenValue> res; Vector<Integer> coordinateVector; String value, pathString; int i,coordinate; Xpth whenPath; res = new Vector<whenValue>(); if ((index < 0)|(index > whenCombinations() - 1)) {throw new MapperException("Invalid index for combination of when-condition values: " + index + " is not in range 0.." + (whenCombinations() - 1));} else { Vector<Integer> ranges = new Vector<Integer>(); Vector<String> pathStrings = new Vector<String>(); Vector<Vector<String>> valueVectors = new Vector<Vector<String>>(); for (Enumeration<String> en = subtreeWhenVals.keys(); en.hasMoreElements();) { pathString = en.nextElement(); Vector<String> values = subtreeWhenVals.get(pathString); pathStrings.addElement(pathString); valueVectors.addElement(values); ranges.addElement(new Integer(values.size())); } coordinateVector = intIndex(ranges,index); for (i = 0; i < ranges.size(); i++) { coordinate = ((Integer)coordinateVector.elementAt(i)).intValue(); Vector<String> values = (Vector<String>)valueVectors.elementAt(i); value = (String)values.elementAt(coordinate); pathString = (String)pathStrings.elementAt(i); whenPath = new Xpth(rootPath.NSSet(),pathString); // if there is a property mapping to this node, use it in the whenValue constructor ClassSet cs = null; String propName = null; for (int j = 0; j < propertyMaps().size(); j++) { propertyMapping pm = (propertyMapping)propertyMaps().elementAt(j); if (whenPath.equalPath(pm.nodePath())) { cs = pm.cSet(); propName = pm.propertyName(); } } res.addElement(new whenValue(whenPath,value,cs,propName)); } } return res; } /* given a vector of ranges (iRange, jRange, kRange...) and an integer 0 < n < (iRange * jRange * ...), return a vector (i,j,k....) such that (0 < i < iRange), (0 < j < jRange), .... and n = i + iRange*(j + jRange*(k + ..))). That is: n = i + iRange* m, (so m = n/iRange; and i = n - m*iRange) m = j + jRange* p .. p = k */ private Vector<Integer> intIndex(Vector<Integer> ranges, int n) { int iRange, m, i; Vector<Integer> res,newRanges; res = new Vector<Integer>(); if ((n > prodSize(ranges) -1)|(n < 0)) {message("Product index error: " + n + " is not between 0 and " + (prodSize(ranges)-1));} else if (ranges.size() == 1) {res.addElement(new Integer(n));} else if (ranges.size() > 1) { iRange = ((Integer)ranges.elementAt(0)).intValue(); m = n/iRange; i = n - m*iRange; newRanges = trimFirst(ranges); res = intIndex(newRanges,m); res.insertElementAt(new Integer(i),0); } return res; } // the product of the values in a Vector of Integers private int prodSize(Vector<Integer> v) { int res = 1; for (int i = 0; i < v.size(); i++) {res =res*((Integer)v.elementAt(i)).intValue();} return res; } // remove the first element from a Vector private Vector<Integer> trimFirst(Vector<Integer> v) { Vector<Integer> w = new Vector<Integer>(); for (int i = 1; i < v.size(); i++) {w.addElement(v.elementAt(i));} return w; } /** write the unique subtree - those nodes below this one with mincardinality = maxCardinality = 1 */ public void writeUniqueSubtree(messageChannel mChan) { mChan.message("Unique subtree structure: "); subTree.writeNested(mChan); } /** write subtree nodes and when values */ public void writeWhenVals(messageChannel mChan) { if (subtreeWhenVals.size() > 0) { message("Subtree nodes and when values: "); for (Enumeration<String> en = subtreeWhenVals.keys();en.hasMoreElements();) { String path = en.nextElement(); Vector<String> vals = subtreeWhenVals.get(path); String line = "Path '" + path + "'; Values: "; for (int i = 0; i < vals.size(); i++) { String val = (String)vals.elementAt(i); line = line + "'" + val + "' "; } mChan.message(line); } } } public void write(messageChannel mChan) { mChan.message("****** USRepSet for node '" + rootPath.stringForm()); String xx = ""; if (noMeaning()) {xx = "No mappings; ";} else {xx = "Has mappings; ";} if (hasDefinitePaths()) {xx = xx + "definite paths; ";} else {xx = xx + "no definite paths; ";} if (hasIndefinitePaths()) {xx = xx + "indefinite paths. ";} else {xx = xx + "no indefinite paths. ";} mChan.message(xx); mChan.message("Def outers: " + maxOuterDefiniteSteps() + " Def inners: " + maxInnerDefiniteStepsToTopNode()); for (int i = 0; i < nodeRepSets.size(); i++) { nodeRepSet nrs = (nodeRepSet)nodeRepSets.elementAt(i); nrs.write(mChan); } } /* the maximum number of inner steps from the top node of the unique subtree to a '//' step which are defined in any mapping in the subtree. Between 1 and rootPath.size() (= rootPath.size() if there are any mappings with definite XPaths)*/ public int maxInnerDefiniteStepsToTopNode() { int res = (rootPath.size() - minUndefinedSteps); // in case the indefinite mappings are to nodes below the root of this subtree if ((res == 0) & hasIndefinitePaths) res = 1; return res; } }