package com.openMap1.mapper.mapping; import com.openMap1.mapper.core.MapperException; import com.openMap1.mapper.core.XpthException; import com.openMap1.mapper.core.Xpth; import com.openMap1.mapper.core.ClassSet; import com.openMap1.mapper.util.GenUtil; import com.openMap1.mapper.util.messageChannel; import com.openMap1.mapper.AssocMapping; import java.util.Iterator; import java.util.Vector; /** * an association (link) mapping. * * This is a wrapper around the model class AssocMapping. * * @author Robert Worden * @version 1.0 */ public class AssociationMapping extends MappingTwo { /** Override: there is no single class or subset associated with an association mapping * - they are associated with the ends. */ public ClassSet cSet() {return null;} /** Override: there is no single class or subset associated with an association mapping * - they are associated with the ends. */ public String className() {return null;} /** Override: there is no single class or subset associated with an association mapping * - they are associated with the ends. */ public String subset() {return null;} public AssocMapping am() {return (AssocMapping)map();} /** * * @param md messageChannel: for writing messages */ public AssociationMapping(AssocMapping am, messageChannel md) throws MapperException { super(am, md); mappingType = MappingTwo.ASSOCIATION; // make the two association end mappings for (int e = 0; e < 2; e++) {assocEnd[e] = new associationEndMapping(am.getMappedEnd(e),md);} // set the opposite end mapping for each end for (int e = 0; e < 2; e++) assocEnd[e].setOtherEndMapping(assocEnd[1-e]); } private associationEndMapping[] assocEnd = new associationEndMapping[2]; /** the association name (not defined in UML) */ public String assocName() {return am().getMappedAssociation();} /** * the association end mapping for one of the ends of the association * * @param end int end = 0 or 1 * * @return associationEndMapping the association end mapping */ public associationEndMapping assocEnd(int end) throws MapperException { associationEndMapping aem = null; if (end == 0) {aem = assocEnd[0];} else if (end == 1) {aem = assocEnd[1];} else {throw new MapperException("Invalid end " + end + " getting mapping for association '" + assocName() + "': " + end);} return aem; } /** * write a simple description of the mapping */ public void write() { mChan().message(""); mChan().message("Association mapping of " + fullName() + " to node " + nodePath().stringForm()); writeConditions(); for (int i = 0; i < 2; i++) try {assocEnd(i).write();} catch(MapperException ex) {} } /** full association name, with classes and subsets at both ends */ public String fullName() { String res = "[undefined]" + assocName() + "[undefined]"; if ((assocEnd[0] != null) && (assocEnd[1] != null)) {res = ("[" + assocEnd[0].getClassSet().stringForm() + "]" + assocName() + "[" + assocEnd[1].getClassSet().stringForm() + "]");} return res; } /** True if this is a self-association, i.e a representation of an association with the same class (and subset) at both ends */ public boolean self() { return (assocEnd[0].getClassSet().equals(assocEnd[1].getClassSet())); } /** a simple nesting association is one in which * <p> * the path from the association node to one object node is a pure ascent (parent:: or ancestor::) * <p> * the path from the association node to the other object is "." (stay here) */ public boolean simpleNesting() throws MapperException { return ((assocEnd[0].pureAscent() & assocEnd[1].identity())| (assocEnd[1].pureAscent() & assocEnd[0].identity())); } /** if either end (0 or 1) of the association has a pure ancestor path, and the other end has an identity path, return the ancestor end 0 or 1. Otherwise return -1. */ public int ancestorEnd() throws MapperException { int j, res; res = -1; for (j = 0; j < 2; j++) if ((assocEnd[j].pureAscent()) && (assocEnd[1-j].identity())) {res = j;} return res; } /** if either end (0 or 1) of the association has an identity path, and the other end has an ancestor path, return the identity end 0 or 1. Otherwise return -1. */ public int identityEnd() throws MapperException { int j, res; res = -1; for (j = 0; j < 2; j++) if ((assocEnd[j].identity()) && (assocEnd[1-j].pureAscent())) {res = j;} return res; } /** find the end = 1 or 2 which has a given role name, or throw an exception */ public int endForRole(String roleName) throws MapperException { int end = -2; for (int i = 0; i < 2; i++) if (assocEnd[i].roleName().equals(roleName)) {end = i + 1;} if (end == -2) {throw new MapperException("Association '" + assocName() + "' has no role '" + roleName + "'");} return end; } /** Ensure that any simple nesting association has the 'stay here' association end marked as 'required', so that the association is an inclusion filter for that class. */ /* void makeSimpleNestingRequired() { if (simpleNesting()) { //message("Required association " + fullName()); if (assocEnd[0].identity()) {assocEnd[0].setRequired(true);} else if (assocEnd[1].identity()) {assocEnd[1].setRequired(true);} } } */ /** XPaths to nodes required to evaluate link conditions for this representation */ public Vector<Xpth> linkConditionPaths() { int i,j; Vector<Xpth> res = new Vector<Xpth>(); for (j = 0; j < 2; j++) { associationEndMapping aem = assocEnd[j]; for (i = 0; i < aem.linkConditions().size(); i++) { linkCondition c = aem.linkConditions().elementAt(i); res.addElement(c.rootToLeftValue()); res.addElement(c.rootToRightValue()); } } return res; } /** If this assocation is required for just one of the objects at the two * ends of the association, return which one (1 or 2) * <p> * Otherwise return 0. * Throw an exception if required for both ends. */ public int requiredEnd() throws MapperException { int i,found,res; found = 0; res = 0; for (i = 0; i < 2; i++) if (assocEnd[i].required()) { found++; res = i+1; } if (found > 0) {throw new MapperException ("Association " + fullName() + " has both ends required.");} return res; } /** simple string description */ public String description() { return("mapping for association " + fullName() ); } /** return 1 or 2 if end 1 or 2 has class and subset cSet. * <p> * return -1 if neither end has this class and subset. * <p> * return 2 if both ends have this class and subset. */ public int classEnd(ClassSet cSet) { int end = -1; for (int i = 0; i < 2; i++) if (assocEnd[i].cSet().equals(cSet)) {end = i + 1;} return end; } /** 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 = super.mappingDepth(); for (int i = 0; i < 2; i++) { if (assocEnd(i).mappingDepth() > depth) depth = assocEnd(i).mappingDepth(); } 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 = super.innerDepth(); for (int i = 0; i < 2; i++) try { if (assocEnd(i).innerDepth() > depth) depth = assocEnd(i).innerDepth(); } catch (MapperException ex) {GenUtil.surprise(ex, "MDLBase.innerDepth");} return depth; } /** return the classSet which has a given role name, or null if there is none */ public ClassSet getClassSet(String roleName) { ClassSet cSet = null; for (int end = 0; end < 2; end++) try { if (roleName.equals(assocEnd(end).roleName())) {cSet = assocEnd(end).cSet();} } catch (MapperException ex) {GenUtil.surprise(ex, "MDLBase.getClassSet");} return cSet; } /** return the class at the other end of the association from the named class, or null if there is none */ public String otherEndClassName(String className) { String oClass = null; for (int end = 0; end < 2; end++) if (assocEnd[end].className().equals(className)) {oClass = assocEnd[1-end].className();} return oClass; } public static Vector<AssociationMapping> vCopyAssociationMapping(Vector<AssociationMapping> v) { Vector<AssociationMapping> res = new Vector<AssociationMapping>(); for (Iterator<AssociationMapping> it = v.iterator();it.hasNext();) res.add(it.next()); return res; } }