package com.openMap1.mapper.health.cda;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.StringTokenizer;
import java.util.Vector;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EReference;
import org.w3c.dom.Element;
import com.openMap1.mapper.core.MapperException;
import com.openMap1.mapper.health.v3.RMIMReader;
import com.openMap1.mapper.util.GenUtil;
import com.openMap1.mapper.util.XMLUtil;
/**
* represents a CDA template, read from a Schematron templates file
*
* @author robert
*
*/
public class CDATemplate {
public boolean tracing() {return templateSet.tracing();}
/**
* @return the full String id of the template
*/
public String fullTemplateId() {return templateId;}
private String templateId;
/**
* @return the local String id of the template, within the template set
*/
public String getLocalId() {return usageElement.getAttribute("id");}
/**
* @return the possible names of the node the template appears on, if known;
* empty Hashtable if not known
*/
public Hashtable<String,String> nodeNames() {return nodeNames;}
private Hashtable<String,String> nodeNames = new Hashtable<String,String>();
public Vector<TemplateRule> getRules() {return rules;}
private Vector<TemplateRule> rules;
// the element in the schematron file (usually a .ent file) defining the template)
private Element templateElement;
// the element in my template usage file defining the template
private Element usageElement;
/**
* @return the level of the template.
* Levels are static constants in CDATemplateSet: CDA, SECTION, etc.
* CDA is the lowest defined level (0) and UNDEFINED is -1.
*/
public int level() {return level;}
private int level;
public String name() {return usageElement.getAttribute("name");}
/**
* @return the template set (eg CCD, HITSP C32) that this template belongs to
*/
public TemplateSet templateSet() {return templateSet;}
private TemplateSet templateSet;
private Vector<TemplatedPath> ownContexts;
/**
* @return true if the full set of CDAContexts at which this template can appear
* has been found, even if it is empty
*/
public boolean resolved() {return resolved;}
private boolean resolved;
/**
* @return descriptive name for this template
*/
public String descriptiveName() {return ("template " + localId() + " (" + name() + ") of set " + templateSet.getName());}
/**
*
* @param temps a Vector of CDATemplates
* @return true if this is one of them
*/
public boolean oneOf(Vector<CDATemplate> temps)
{
boolean oneOf = false;
for (Iterator<CDATemplate> it = temps.iterator();it.hasNext();)
{
CDATemplate temp = it.next();
if ((temp.localId().equals(localId()))
&& (temp.templateSet().getName().equals(templateSet().getName()))) oneOf = true;
}
return oneOf;
}
public String key() {return (name() + "_" + templateSet().getName());}
//--------------------------------------------------------------------------------------------
// Constructors
//--------------------------------------------------------------------------------------------
/**
* usual constructor when there is a .ent file for the template
*/
public CDATemplate(TemplateSet templateSet, String templateId, Element templateElement, Element usageElement, int level)
throws MapperException
{
this.templateSet = templateSet;
this.templateId = templateId;
this.templateElement = templateElement;
this.usageElement = usageElement;
checkUsageElement(usageElement);
this.level = level;
rules = new Vector<TemplateRule>();
ownContexts = new Vector<TemplatedPath>();
nodeNames = new Hashtable<String,String>();
resolved = false;
// trace("");
// trace("Reading template " + templateId + " in set " + templateSet.getName());
}
/**
* constructor for when there is no .ent file defining the template, but it only constrains
* some existing templates.
* @param templateSet
* @param usageElement
* @param level
* @throws MapperException
*/
public CDATemplate(TemplateSet templateSet, Element usageElement, int level)
throws MapperException
{
this.templateSet = templateSet;
templateId = usageElement.getAttribute("id");
if (!templateSet.idBase().equals("")) templateId = templateSet.idBase() + "." + templateId;
this.usageElement = usageElement;
checkUsageElement(usageElement);
this.level = level;
rules = new Vector<TemplateRule>();
ownContexts = new Vector<TemplatedPath>();
nodeNames = new Hashtable<String,String>();
resolved = false;
}
private void checkUsageElement(Element usageElement) throws MapperException
{
String[] usageAtts = {"id","name","fromFile","nodeNames"};
String[] usageChildren = {"constrains","in","canCoexist"};
XMLUtil.checkAttributes(usageElement, usageAtts);
XMLUtil.checkChildElements(usageElement, usageChildren);
}
//--------------------------------------------------------------------------------------------
// recording rules
//--------------------------------------------------------------------------------------------
public void recordRules() throws MapperException
{
Vector<Element> ruleEls = XMLUtil.namedChildElements(templateElement, "rule");
for (Iterator<Element> it = ruleEls.iterator();it.hasNext();)
rules.add(new TemplateRule(this,templateId,it.next()));
}
/**
* @return the assertions on rules which have the standard context (i.e rules
* which apply on the template node) and which define the node the rule applies on -
* and therefore define the template node
*/
public Vector<TemplateAssertion> definingAssertions()
{
Vector<TemplateAssertion> defining = new Vector<TemplateAssertion>();
for (Iterator<TemplateRule> ir = rules.iterator();ir.hasNext();)
{
TemplateRule tr = ir.next();
if (tr.isStandardContext())
for (Iterator<TemplateAssertion> ia = tr.definingAssertions().iterator();ia.hasNext();)
defining.add(ia.next());
}
return defining;
}
public Vector<TemplateAssertion> constrainingAssertions()
{
Vector<TemplateAssertion> constraining = new Vector<TemplateAssertion>();
for (Iterator<TemplateRule> ir = rules.iterator();ir.hasNext();)
{
TemplateRule tr = ir.next();
for (Iterator<TemplateAssertion> ia = tr.constrainingAssertions().iterator();ia.hasNext();)
constraining.add(ia.next());
}
return constraining;
}
//--------------------------------------------------------------------------------------------
// resolving possible contexts for this template
//--------------------------------------------------------------------------------------------
/**
* attempt to set the possible node names of the template from its defining assertions
*/
public void setNodeNames()
{
addNodeNamesFromUsageFile();
for (Iterator<TemplateAssertion> it = definingAssertions().iterator(); it.hasNext();)
{
TemplateAssertion assertion = it.next();
TestNode assertNode = assertion.testNode();
// 'self::' XPath; should be only one possible node name
if (assertNode.connector() == TestNode.XPATH) addNodeName(assertNode);
// 'and' or 'or' of XPaths; if any have an initial 'self' step, add possible node names
if ((assertNode.connector() == TestNode.OR)|(assertNode.connector() == TestNode.AND))
{
for (Iterator<TestNode> ic = assertNode.childNodes().iterator();ic.hasNext();)
{
TestNode child = ic.next();
if (child.connector() == TestNode.XPATH) addNodeName(child);
}
}
}
}
/**
* Define some node names on which this template may appear,
* using an attribute in the template usage file
* (for cases where the template itself fails to define the nodes it appears on).
* It is OK if the template usage file duplicates node names defined in the template assertions
*/
private void addNodeNamesFromUsageFile()
{
nodeNames = new Hashtable<String,String>();
String names = usageElement.getAttribute("nodeNames");
if (!names.equals(""))
{
StringTokenizer st = new StringTokenizer(names," ");
while (st.hasMoreTokens())
{
String nodeName = st.nextToken();
nodeNames.put(nodeName,nodeName);
}
}
}
/**
* if the TestNode is an XPath with an initial 'self::' step,
* add the defined node name to the list of possible node names for this template
* @param assertNode a TestNode
*/
private void addNodeName(TestNode assertNode)
{
if (assertNode.connector() == TestNode.XPATH)
{
TestNode firstStep = assertNode.childNodes().get(0);
if ((firstStep.axis() == TestNode.SELF) && (firstStep.hasNodeTest()))
{
StringTokenizer st = new StringTokenizer(firstStep.nodeTest(),":");
// remove the namespace prefix if there is any
String nodeName = st.nextToken();
if (st.hasMoreTokens()) nodeName = st.nextToken();
nodeNames.put(nodeName,nodeName);
}
}
}
/**
* @return a Vector of all the templates (in any template set)
* that this template can appear directly inside, as defined directly for this
* template in the template usage file
*/
public Vector<CDATemplate> ownParentTemplates() throws MapperException
{
Vector<CDATemplate> parents = new Vector<CDATemplate>();
for (Iterator<Element> it = XMLUtil.namedChildElements(usageElement, "in").iterator();it.hasNext();)
{
CDATemplate temp = getTemplateFromInElement(it.next());
if (temp != null) parents.add(temp);
}
return parents;
}
/**
*
* @return all parent templates of this template, or of any others
* that it constrains
* @throws MapperException
*/
public Vector<CDATemplate> allParentTemplates() throws MapperException
{
Vector<CDATemplate> parents = ownParentTemplates();
for (Iterator<CDATemplate> it = constrainedTemplates().iterator();it.hasNext();)
{
for (Iterator<CDATemplate> iu = it.next().allParentTemplates().iterator(); iu.hasNext();)
{
CDATemplate parent = iu.next();
if (!parent.oneOf(parents)) parents.add(parent);
}
}
return parents;
}
/**
* @return a Vector of all the templates (in any template set)
* that this template provides further constraints on; wherever this
* template appears ,those templates must appear also
*/
public Vector<CDATemplate> directConstrainedTemplates() throws MapperException
{
Vector<CDATemplate> constrained = new Vector<CDATemplate>();
for (Iterator<Element> it = XMLUtil.namedChildElements(usageElement, "constrains").iterator();it.hasNext();)
{
String constId = XMLUtil.getText(it.next());
CDATemplate temp = templateSet.collection().getTemplate(constId);
if (temp == null) throw new MapperException("Cannot find template '" + constId
+ "' directly constrained by template '" + fullTemplateId() + "'");
constrained.add(temp);
}
return constrained;
}
/**
* recursive descent of the 'constrains' relation (which must not be circular - that gets checked)
* @return all templates that this template constrains, directly or indirectly
* @throws MapperException
*/
public Vector<CDATemplate> constrainedTemplates() throws MapperException
{
Vector<CDATemplate> constrained = directConstrainedTemplates();
Vector<CDATemplate> constrained2 = directConstrainedTemplates();
for (Iterator<CDATemplate> it = constrained.iterator();it.hasNext();)
{
CDATemplate con = it.next();
if (con==null) throw new MapperException("null template");
Vector<CDATemplate> all = con.constrainedTemplates();
for (Iterator<CDATemplate> iu = all.iterator();iu.hasNext();)
{
CDATemplate next = iu.next();
if (!hasTemplateId(next.fullTemplateId(),constrained2)) constrained2.add(next);
}
}
return constrained2;
}
/**
* @return a Vector of all the templates (in any template set)
* that may appear on the same node as this template,
* but need not necessarily do so
*/
public Vector<CDATemplate> coexistentTemplates() throws MapperException
{
Vector<CDATemplate> constrained = new Vector<CDATemplate>();
for (Iterator<Element> it = XMLUtil.namedChildElements(usageElement, "canCoexist").iterator();it.hasNext();)
{
String constId = XMLUtil.getText(it.next());
CDATemplate temp = templateSet.collection().getTemplate(constId);
if (temp == null) throw new MapperException("Cannot find template '" + constId
+ "' stated to coexist with template '" + fullTemplateId() + "'");
constrained.add(temp);
}
return constrained;
}
public boolean canCoexist(CDATemplate other) throws MapperException
{
boolean coexist = false;
for (Iterator<CDATemplate> it = coexistentTemplates().iterator(); it.hasNext();)
if (it.next().fullTemplateId().equals(other.fullTemplateId())) coexist = true;
return coexist;
}
/**
* @param id
* @param temps
* @return true if the template id is in the set of templates
*/
private boolean hasTemplateId(String id, Vector<CDATemplate> temps)
{
boolean hasId = false;
for (Iterator<CDATemplate> it = temps.iterator();it.hasNext();)
{
CDATemplate tem = it.next();
if (tem.fullTemplateId().equals(id)) hasId = true;
}
return hasId;
}
/**
*
* @param inEl
* @return
* @throws MapperException
*/
private CDATemplate getTemplateFromInElement(Element inEl) throws MapperException
{
// find the right template set - this one if not specified
String setName = inEl.getAttribute("set");
String localId = XMLUtil.getText(inEl);
if (setName.equals("")) setName = templateSet.getName();
TemplateSet ts = templateSet.collection().getTemplateSet(setName);
if (ts == null) throw new MapperException("There is no template set " + setName);
// use the local id in the template set, only if that set uses local ids
CDATemplate temp = ts.getTemplateByLocalId(localId);
// otherwise look for the full template id, in the whole collection
if (temp == null) temp = ts.collection().getTemplate(localId);
// failure
if (temp == null) throw new MapperException("There is no template with local or full id " + localId
+ " in template set " + setName + " or in the whole collection");
return temp;
}
/**
* @param parentTemplate a template that is a defined parent of this one
* @return the allowed paths from the parent template to this one,
* if they have been defined in the appropriate 'nodePaths' attribute of the Template usage file;
* or an empty Vector if they have not.
*/
public Vector<String> definedPathsFromTemplate(CDATemplate parentTemplate) throws MapperException
{
Vector<String> paths = new Vector<String>();
for (Iterator<Element> it = XMLUtil.namedChildElements(usageElement, "in").iterator();it.hasNext();)
{
Element inEl = it.next();
String[] inAtts = {"nodePaths","set"};
XMLUtil.checkAttributes(inEl, inAtts);
CDATemplate temp = getTemplateFromInElement(inEl);
if ((temp != null) && (temp.fullTemplateId().equals(parentTemplate.fullTemplateId())))
{
StringTokenizer st = new StringTokenizer(inEl.getAttribute("nodePaths")," ");
while (st.hasMoreTokens()) paths.add(st.nextToken());
}
}
return paths;
}
/**
* @param paths a Vector of paths from a start class
* @param startClass the start class
* @param lastAssoc the last association leading to the start class
* @param nodeName must match the final step of a path, for the path to count
* @return a Vector of CDAContexts corresponding to those paths that can be followed
* through associations and which end in the required node name
*/
private Vector<TemplatedPath> definedContexts(Vector<String>paths, EClass startClass,
String lastAssoc, String nodeName)
{
Vector<TemplatedPath> contexts = new Vector<TemplatedPath>();
for (Iterator<String> ip = paths.iterator(); ip.hasNext();)
{
TemplatedPath con = new TemplatedPath().addStep(new ContextStep(lastAssoc,startClass.getName()));
EClass current = startClass;
String path = ip.next();
String step = "";
StringTokenizer steps = new StringTokenizer(path,"/");
boolean found = true;
while (steps.hasMoreTokens())
{
step= steps.nextToken();
if (found)
{
found = false; // remains false if you cannot follow any step in the path
EClass nextClass = null;
for (Iterator<EReference> ir = current.getEAllReferences().iterator();ir.hasNext();)
{
EReference ref = ir.next();
String refName = ref.getName();
if ((!found) && (refName.equals(step)))
{
found = true;
nextClass = (EClass)ref.getEType();
String className = nextClass.getName();
con.addStep(new ContextStep(step,className));
}
}
if (found) current = nextClass;
}
}
// if all steps could be followed and the last step matched the required node name...
if ((found) && (step.equals(nodeName))) contexts.add(con);
}
return contexts;
}
/**
* @return true if all the templates that this template can appear directly
* inside have been resolved
*/
public boolean parentsAllResolved() throws MapperException
{
boolean parentsResolved = true;
for (Iterator<CDATemplate> it = allParentTemplates().iterator();it.hasNext();)
if (!it.next().resolved()) parentsResolved = false;
return parentsResolved;
}
/**
* For the root template of some template set, set its context to be the root context.
* @param rootContext
*/
public void resolveRootContext(TemplatedPath rootContext)
{
ownContexts = new Vector<TemplatedPath>();
ownContexts.add(rootContext);
resolved = true;
}
/**
* resolve this template: find all the full contexts in which it can appear.
* made from the unmodified Ecore model
*/
public void resolve() throws MapperException
{
resolved = true; // record you have attempted resolution, whether or not any contexts are found
ownContexts = new Vector<TemplatedPath>();
for (Iterator<CDATemplate> it = allParentTemplates().iterator(); it.hasNext();)
{
CDATemplate parent = it.next();
Vector<String> paths = definedPathsFromTemplate(parent);
for (Iterator<TemplatedPath> ip = parent.allContexts().iterator();ip.hasNext();)
{
TemplatedPath parentContext = ip.next();
int last = parentContext.length() - 1;
String lastAssoc = parentContext.step(last).associationName();
EClass startClass = findEndClass(parentContext);
/* key = a node name; element = all the CDAContexts that lead from the start class
* (the first step) to the node name, without repeating node names */
Hashtable<String,Vector<TemplatedPath>> extensionContexts =
getContextExtensions(startClass,lastAssoc);
// traceKeys(extensionContexts);
// for each possible node name of this template....
for (Enumeration<String> en = nodeNames.keys();en.hasMoreElements();)
{
String nodeName = en.nextElement();
// heuristic: find the contexts ending in the node name...
Vector<TemplatedPath> targetContexts = extensionContexts.get(nodeName);
/* .. and pick out the joint shortest of those contexts, but with length > 1,
* because length = 1 ends on the start class */
Vector<TemplatedPath> chosenContexts = shortestLongerThan1(targetContexts);
/* If the template usage file defines any paths from the parent class,
* convert these paths into CDAContexts, which override those made by the heuristic */
if (paths.size() > 0) chosenContexts = definedContexts(paths,startClass,lastAssoc,nodeName);
// for each of the chosen extensions, extend the cloned parent context
for (int s = 0; s < chosenContexts.size(); s++)
{
TemplatedPath extension = chosenContexts.get(s);
TemplatedPath parentClone = parentContext.clone();
// note that the parent template must now appear on its last step
parentClone.step(last).addTemplateId(parent.fullTemplateId());
/* do not add step 0 of the extension context,
* which duplicates the last step of the parent context; but
* because the extension has length > 1, you will add some steps */
for (int w = 1; w < extension.length();w++)
parentClone.addStep(extension.step(w));
ownContexts.add(parentClone);
}
}
}
}
}
/**
* @param longList a list of CDAContexts, of different lengths
* @return all those that tie for the shortest length > 1
*/
private Vector<TemplatedPath> shortestLongerThan1(Vector<TemplatedPath> longList)
{
Vector<TemplatedPath> shortest = new Vector<TemplatedPath>();
if (longList != null)
{
// find the shortest length > 1
int minLength = 1000;
for (int i = 0; i < longList.size(); i++)
{
TemplatedPath context = longList.get(i);
if ((context.length() > 1) && (context.length() < minLength))
minLength = context.length();
}
// collect all those that have the shortest length > 1
for (int i = 0; i < longList.size(); i++)
if (longList.get(i).length() == minLength) shortest.add(longList.get(i));
}
return shortest;
}
//---------------------------------------------------------------------------------------------
// Extending template contexts
//---------------------------------------------------------------------------------------------
/**
* @param eClass a class which has a template on it
* @param assocName the association that leads to the EClass
* @return key = a node name; element = all the CDAContexts that lead from above the start class
* via the association name to the node name, without repeating node names
*/
private Hashtable<String,Vector<TemplatedPath>> getContextExtensions(EClass eClass, String assocName)
{
Hashtable<String,Vector<TemplatedPath>> extensionContexts = new Hashtable<String,Vector<TemplatedPath>>();
TemplatedPath empty = new TemplatedPath();
extendContexts(extensionContexts,empty,eClass,assocName,0);
return extensionContexts;
}
/**
* Recursively extend the Hashtable 'contexts' for the new class - avoiding contexts that repeat any class
* @param contexts for each node name, a Vector of all the contexts that reach that node.
* Initially called with contexts empty, but recurses
* @param context a CDAContext that leads down to some EClass;
* initially called with context empty (no steps), but recurses
* @param eClass another EClass, reached by a containment association from
* the EClass at the end of the context
* @param assocName the association name
* @param depth to limit recursion depth
*/
private void extendContexts(Hashtable<String,Vector<TemplatedPath>> contexts,
TemplatedPath context, EClass eClass, String assocName,int depth)
{
int maxDepth = 5; // no templates nested inside other templates as depth > 4
// add a context with a final step for the new class
ContextStep step = new ContextStep(assocName,eClass.getName());
step.setFixedValues(eClass);
TemplatedPath newContext = context.clone();
newContext.addStep(step);
// contexts are stored by inner association name, except the top context that has no association
String keyName = assocName;
if (keyName == null) keyName = eClass.getName();
Vector<TemplatedPath> namedContexts = contexts.get(keyName);
if (namedContexts == null) namedContexts = new Vector<TemplatedPath>();
namedContexts.add(newContext);
contexts.put(keyName,namedContexts);
/* extend for all the containment relations of the new class, which do not repeat classes more than twice;
* exclude all data type classes. */
for (Iterator<EReference> it = eClass.getEAllReferences().iterator();it.hasNext();)
{
EReference ref = it.next();
EClassifier target = ref.getEType();
if ( (ref.isContainment())
&& (depth < maxDepth)
&& (newContext.classCount(target.getName()) < 3)
&& (target instanceof EClass)
&& (!(target.getEPackage().getName().equals(RMIMReader.DATATYPE_PACKAGE_NAME))))
extendContexts(contexts,newContext,(EClass)target,ref.getName(),depth + 1);
}
}
//--------------------------------------------------------------------------------------------
// access methods
//--------------------------------------------------------------------------------------------
/**
* @return the set of all contexts at which this template may appear;
* assuming that if it constrains another template, it can appear in all the same
* contexts as that other template
* warning - will blow up if two contexts constrain one another; the 'constrains'
* relation must not be circular
*/
public Vector<TemplatedPath> allContexts() throws MapperException
{
Vector<TemplatedPath> all = ownContexts;
for (Iterator<CDATemplate> it = constrainedTemplates().iterator();it.hasNext();)
{
CDATemplate templ = it.next();
Vector<TemplatedPath> other = templ.allContexts();
for (Iterator<TemplatedPath> iu = other.iterator();iu.hasNext();)
{
TemplatedPath con = iu.next();
if (!con.inContexts(all)) all.add(con);
}
}
return all;
}
/**
* This template is compatible with a CDAContext if for one of the resolved contexts
* of this template:
* (a) There is the same number of steps
* (b) For each step, the step name (association name or class name) matches
* (c) For each step, all the template ids required by this template's context are supplied by the context
* (d) For each step, all the fixed values required by this template's context are supplied by the context
*/
public boolean isCompatibleWithContext(TemplatedPath context) throws MapperException
{
for (Iterator<TemplatedPath> it = allContexts().iterator();it.hasNext();)
{
TemplatedPath thisContext = it.next();
if (context.length() == thisContext.length())
{
boolean matches = true;
for (int s = 0; s < context.length(); s++)
if (!(thisContext.step(s).compatibleStep(context.step(s)))) matches = false;
if (matches) return true;
}
}
return false;
}
/**
* @return the subset of the contexts of this template which could compatibly match
* or extend the given context, in that:
* (a) They have the same number of steps as the given context, or more steps
* (b) For each step in the given context, the step name (association name or class name) matches
* (c) For each step in the given context, all the template ids required by this template's context are supplied by the context
* (d) For each step in the given context, all the fixed values required by this template's context are supplied by the context
*/
public Vector<TemplatedPath> extendingContexts(TemplatedPath context) throws MapperException
{
Vector<TemplatedPath> extenders = new Vector<TemplatedPath>();
for (Iterator<TemplatedPath> it = allContexts().iterator();it.hasNext();)
{
TemplatedPath thisContext = it.next();
if (context.length() < thisContext.length() + 1)
{
boolean matches = true;
for (int s = 0; s < context.length(); s++)
if (!(thisContext.step(s).compatibleStep(context.step(s)))) matches = false;
if (matches) extenders.add(thisContext);
}
}
return extenders;
}
/**
* @param temp some other template
* @return true if this template and the other can be on the same node
* This is true if either template constrains the other, or if it has been stated
* in the template usage file that either can coexist with the other.
* A template can coexist with itself.
*/
public boolean canBeOnSameNodeAs(CDATemplate temp) throws MapperException
{
boolean OK = ((constrains(temp))|(temp.constrains(this)));
if (canCoexist(temp)) OK = true;
if (temp.canCoexist(this)) OK = true;
if (temp.templateId.equals(this.fullTemplateId())) OK = true;
return OK;
}
/**
*
* @param temp some other template
* @return true if this template constrains the other template, so can appear on the
* same node
*/
public boolean constrains(CDATemplate temp) throws MapperException
{
boolean constrains = false;
Vector<CDATemplate> constrained = constrainedTemplates();
for (Iterator<CDATemplate> it = constrained.iterator(); it.hasNext();)
if (it.next().fullTemplateId().equals(temp.fullTemplateId())) constrains = true;
return constrains;
}
/**
* @return all possible node names for the template, concatenated and separated by Strings
*/
public String allNodeNames()
{
String allNames = "";
for (Enumeration<String> en = nodeNames.keys();en.hasMoreElements();)
allNames = allNames + en.nextElement() + " ";
return allNames;
}
/**
*
* @param context a CDAContext starting at the root class
* @return the EClass at the end of the context path
*/
private EClass findEndClass(TemplatedPath context)
{
EClass currentClass = templateSet().collection().getEntryClass();
for (int s = 1; s < context.length();s++)
{
ContextStep step = context.step(s);
for (Iterator<EReference> ir = currentClass.getEAllReferences().iterator();ir.hasNext();)
{
EReference ref = ir.next();
if ((ref.isContainment()) && (ref.getName().equals(step.associationName())))
currentClass = (EClass)ref.getEType();
}
}
return currentClass;
}
/**
* @return the last numeral in a template id
*/
public String localId()
{
String localId = "";
StringTokenizer st = new StringTokenizer(fullTemplateId(),".");
while (st.hasMoreTokens()) localId = st.nextToken();
return localId;
}
//--------------------------------------------------------------------------------------------
// annotating the Ecore model with template constraints
//--------------------------------------------------------------------------------------------
/**
* add annotations for each constraint the template puts on descendant nodes
* @param templatedClass the EClass to receive the annotations
* @param suffix a String "", or "_1", "_2" etc defining which of the templates
* on the EClass provides each constraint
*/
public void addConstraintAnnotations(TemplatedPath context,EClass templatedClass,String suffix)
{
for (Iterator<TemplateRule> it = rules.iterator();it.hasNext();)
{
TemplateRule rule = it.next();
rule.addRuleConstraintAnnotations(context, templatedClass, suffix);
}
}
//--------------------------------------------------------------------------------------------
// Extracting information for an initial template usage file, to be edited
//--------------------------------------------------------------------------------------------
private int heuristicLevel;
/**
* @return a guess at the level of a template, based on words in its title
*/
public int heuristicLevel()
{
getTrialTitle();
return heuristicLevel;
}
/**
* return the title of a template, excluding spaces and common words
*/
public String getTrialTitle()
{
heuristicLevel = 3;
String[] dropWords= {"IHE", "PCC", "-", "errors", "validation", "phase"};
String title = "";
Element titEl = XMLUtil.firstNamedChild(templateElement, "title");
if (titEl != null)
{
StringTokenizer st = new StringTokenizer(XMLUtil.getText(titEl)," ");
while (st.hasMoreTokens())
{
String word = st.nextToken();
if (!GenUtil.inArray(word, dropWords)) title = title + word;
// allocate heuristic levels, depending on what is in the title
if (word.equalsIgnoreCase("Section")) heuristicLevel = 1;
if (word.equalsIgnoreCase("Entry")) heuristicLevel = 2;
if (word.equalsIgnoreCase("Activity")) heuristicLevel = 2;
if (word.equalsIgnoreCase("Organizer")) heuristicLevel = 2;
// default is 3
}
}
return title;
}
public Vector<String> constrainedTemplateIds()
{
String start = "cda:templateId[@root=";
Vector<String> ids = new Vector<String>();
Vector<Element> rules = XMLUtil.namedChildElements(templateElement, "rule");
if (rules.size() == 1)
{
Vector<Element> asserts = XMLUtil.namedChildElements(rules.get(0), "assert");
for (Iterator<Element> it = asserts.iterator(); it.hasNext();)
{
String test = it.next().getAttribute("test");
StringTokenizer parts = new StringTokenizer(test," ");
while (parts.hasMoreTokens())
{
String part = parts.nextToken();
if (part.startsWith(start))
{
// strip off start and first quote
String id = part.substring(start.length() + 1);
// strip off any trailing 'and'
if (id.endsWith("and")) id = id.substring(0, id.length()-3);
// strip off last quote and bracket
id = id.substring(0,id.length()-2);
ids.add(id);
}
}
}
}
return ids;
}
//--------------------------------------------------------------------------------------------
// trivia
//--------------------------------------------------------------------------------------------
@SuppressWarnings("unused")
private void trace(String s) {if (tracing()) System.out.println(s);}
}