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.w3c.dom.Element;
import com.openMap1.mapper.core.MapperException;
import com.openMap1.mapper.util.GenUtil;
import com.openMap1.mapper.util.XMLUtil;
/**
* represents a set of CDA templates from one source, eg the CCD templates or
* HITP C32 templates.
*
* @author robert
*
*/
public class TemplateSet {
public boolean tracing() {return collection.tracing();}
/**
* @param templateId a global template id
* @return the template with that global id
*/
public CDATemplate getTemplateByFullId(String templateId) {return templates.get(templateId);}
public Hashtable<String,CDATemplate> templates() {return templates;}
private Hashtable<String,CDATemplate> templates;
/* template patterns have phases like 'errors', 'warning, 'manual', 'note';
* some phases are to be ignored for the purposes of defining the constrained RMIM. */
private String[] phasesRead = {"errors"};
private Element templateSetEl;
/**
* @return full id of the root template (defined by the 'rootTemplate' attribute of the <templateSet> element)
*/
public String rootTemplateId() {return rootTemplateId;}
private String rootTemplateId;
/**
* @return the string root
* of all non-root ids in the tempate set
* (defined by the attribute 'idBase' of the <templateSet> element)
*/
public String idBase() {return idBase;}
private String idBase;
public static String[] levelNames = {"CDA","section","entry","subEntry"};
/**
* levels of templates
*/
public static int UNDEFINED = -1;
public static int CDA = 0;
public static int SECTION = 1;
public static int ENTRY = 2;
public static int SUB_ENTRY = 3;
public String getName() {return templateSetEl.getAttribute("name");}
public TemplateCollection collection() {return collection;}
private TemplateCollection collection;
//------------------------------------------------------------------------------------------------
// Constructor
//------------------------------------------------------------------------------------------------
/**
* @param topTemplatePath file path to the top template file for this template set
* @param templateSetEl element describing the template set in the template usage file
*
*/
public TemplateSet(TemplateCollection collection, String topTemplatePath,Element templateSetEl)
throws MapperException
{
String[] setAtts = {"name","schFileName","subFolder","rootTemplate","idBase"};
String[] setChildren = {"templateLevel"};
XMLUtil.checkChildElements(templateSetEl, setChildren);
XMLUtil.checkAttributes(templateSetEl, setAtts);
this.collection = collection;
this.templateSetEl = templateSetEl;
idBase = templateSetEl.getAttribute("idBase"); // may be "" if base ids are not used
rootTemplateId = templateSetEl.getAttribute("rootTemplate");
if (rootTemplateId.equals("")) rootTemplateId = idBase;
templates = new Hashtable<String,CDATemplate>();
// read the template file and store all templates mentioned in the template usage file
readTemplateFile(topTemplatePath, false);
}
/**
* @param topTemplatePath file path to the top template file for this template set
* read the template file and store all templates mentioned in the template usage file
* @param readAllTemplates if true, read all templates even if they are not mentioned
* in the template usage file
* @throws MapperException
*/
public void readTemplateFile(String topTemplatePath, boolean readAllTemplates)
throws MapperException
{
trace("Reading template file at " + topTemplatePath);
Element rootElement = XMLUtil.getRootElement(topTemplatePath);
// templates for which the .sch template file refers to a .ent template
Vector<Element> children = XMLUtil.namedChildElements(rootElement, "pattern");
trace("Pattern elements: " + children.size());
for (Iterator<Element> it = children.iterator();it.hasNext();)
{
Element child = it.next();
String patternId = child.getAttribute("id");
String templateFullId = getFullTemplateId(patternId);
trace("Template full id " + templateFullId);
/* only read templates that are in the template usage file,
* unless readAllTemplates is true when making an initial template usage file */
Element usageElement = getTemplateUsageElement(templateFullId);
int level = getTemplateLevel(templateFullId);
if ((usageElement != null)|(readAllTemplates))
{
String phase = getTemplatePhase(patternId);
// currently only read rules in the patterns for the 'errors' phase
if (GenUtil.inArray(phase, phasesRead))
{
CDATemplate template = templates.get(templateFullId);
if (template == null) template = new CDATemplate(this,templateFullId,child,usageElement,level);
template.recordRules();
templates.put(templateFullId, template);
}
}
}
// templates in the template usage file, for which no .ent file exists
for (Iterator<Element> it = XMLUtil.namedChildElements(templateSetEl, "templateLevel").iterator(); it.hasNext();)
{
Element levelEl = it.next();
String level = levelEl.getAttribute("level");
int lev = UNDEFINED;
for (int i = 0; i < levelNames.length; i++) if (level.equals(levelNames[i])) lev = i;
trace("Templates at level " + level);
for (Iterator<Element> iu = XMLUtil.namedChildElements(levelEl,"template").iterator();iu.hasNext();)
{
Element useElement = iu.next();
if (useElement.getAttribute("fromFile").equals("no"))
{
CDATemplate templ = new CDATemplate(this, useElement, lev);
templates.put(templ.fullTemplateId(), templ);
trace("Template with no file " + templ.fullTemplateId());
}
}
}
writeTemplateSummary();
}
/**
* @param patternId value of the 'id' attribute of a <pattern> element,
* with values such as 'p-2.16.840.1.113883.10.20.1.2-errors'
* @return the template id
* @throws MapperException
*/
private String getFullTemplateId(String patternId)
throws MapperException
{
StringTokenizer st = new StringTokenizer(patternId,"-");
if (st.countTokens() != 3) throw new MapperException("Unexpected form of pattern id: " + patternId);
st.nextToken(); // ignore the initial 'p'
return st.nextToken();
}
/**
* @param patternId value of the 'id' attribute of a <pattern> element
* with values such as 'p-2.16.840.1.113883.10.20.1.2-errors'
* @return the phase of the template - 'errors', 'warning' and so on
* @throws MapperException
*/
private String getTemplatePhase(String patternId)
throws MapperException
{
StringTokenizer st = new StringTokenizer(patternId,"-");
if (st.countTokens() != 3) throw new MapperException("Unexpected form of pattern id: " + patternId);
st.nextToken();st.nextToken(); // ignore the initial 'p' and the template id
return st.nextToken();
}
/**
* @param fullTemplateId a full template id string
* @return the template element in the template usage file; or null if there is none
*/
private Element getTemplateUsageElement(String fullTemplateId) throws MapperException
{
Element usageElement = null;
for (Iterator<Element> it = XMLUtil.namedChildElements(templateSetEl, "templateLevel").iterator();it.hasNext();)
{
Element levelEl = it.next();
String[] levelAtts = {"level"};
String[] levelChildren = {"template"};
XMLUtil.checkAttributes(levelEl, levelAtts);
XMLUtil.checkChildElements(levelEl, levelChildren);
for (Iterator<Element> is = XMLUtil.namedChildElements(levelEl, "template").iterator();is.hasNext();)
{
Element candidate = is.next();
if (candidate.getAttribute("id").equals(getLocalId(fullTemplateId))) usageElement = candidate;
}
}
return usageElement;
}
/**
* @param fullTemplateId a full template id string
* @return the static integer 'CDA', 'SECTION' etc describing the level of the template
*/
private int getTemplateLevel(String fullTemplateId)
{
String level = "";
for (Iterator<Element> it = XMLUtil.namedChildElements(templateSetEl, "templateLevel").iterator();it.hasNext();)
{
Element levelEl = it.next();
String candLevel = levelEl.getAttribute("level");
for (Iterator<Element> is = XMLUtil.namedChildElements(levelEl, "template").iterator();is.hasNext();)
{
Element candidate = is.next();
if (candidate.getAttribute("id").equals(getLocalId(fullTemplateId))) level = candLevel;
}
}
int lev = UNDEFINED;
for (int i = 0; i < levelNames.length; i++) if (level.equals(levelNames[i])) lev = i;
return lev;
}
/**
* @param fullTemplateId
* @return the local id used for template ids in this set in the template usage file
*/
private String getLocalId(String fullTemplateId)
{
String localId = fullTemplateId;
// the local id of the root template is 'root'
if (fullTemplateId.equals(rootTemplateId)) localId = "root";
// for all other templates, strip off the base and '.' to get the local id (if you can)
else if ((!idBase.equals("")) && (fullTemplateId.startsWith(idBase)))
localId = fullTemplateId.substring(idBase.length() + 1);
return localId;
}
/**
* @param localId a local template id such as '5', as used in the template usage file
* for this template set
* @return the template with that local id
*/
public CDATemplate getTemplateByLocalId(String localId)
{
String fullTemplateId;
if (localId.equals("root")) fullTemplateId = rootTemplateId;
else
{
if (idBase.equals("")) fullTemplateId = localId;
else fullTemplateId = idBase + "." + localId;
}
return getTemplateByFullId(fullTemplateId);
}
private void writeTemplateSummary() throws MapperException
{
trace("");
trace("Summary of template set '" + getName() + "'");
for (Enumeration<String> en = templates.keys();en.hasMoreElements();)
{
String fullTemplateId = en.nextElement();
String localId = getLocalId(fullTemplateId);
CDATemplate temp = templates.get(fullTemplateId);
int defs = temp.definingAssertions().size();
int cons = temp.constrainingAssertions().size();
trace("template " + localId + "(" + fullTemplateId + ") defines: " + defs + "; constrains: " + cons);
if ((defs == 0) && (cons > 0))
{
for (Iterator<TemplateAssertion> it = temp.definingAssertions().iterator();it.hasNext();)
{
TemplateAssertion ta = it.next();
trace(ta.testNode().stringForm());
}
}
}
trace("End of template summary");
}
private void trace(String s) {if (tracing()) System.out.println(s);}
}