package com.openMap1.mapper.health.actions;
import java.io.File;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.StringTokenizer;
import java.util.Vector;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.emf.common.util.URI;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.xsd.XSDSchema;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.openMap1.mapper.core.MapperException;
import com.openMap1.mapper.health.cda.CDATemplate;
import com.openMap1.mapper.health.cda.TemplateCollection;
import com.openMap1.mapper.health.cda.TemplateSet;
import com.openMap1.mapper.health.v3.RMIMReader;
import com.openMap1.mapper.structures.XSDStructure;
import com.openMap1.mapper.util.EclipseFileUtil;
import com.openMap1.mapper.util.FileUtil;
import com.openMap1.mapper.util.XMLUtil;
import com.openMap1.mapper.views.WorkBenchUtil;
/**
* Action to make an Ecore class model from a V3 RMIM (.mif file)
*
* @author robert
*
*/
public class MakeEcoreFromRMIMActionDelegate
implements IObjectActionDelegate{
public IWorkbenchPart targetPart; // where this action was invoked from
public ISelection selection;
// if true, use a Java mapping class rather than making all the mappings explicitly
private boolean useJavaMappings = true;
@Override
public void run(IAction action) {
String mifFilePath = "";
Element templateUsageRoot = null;
/* (0) check that there is a data types schema at the expected location in the project
* (this is just a check; the result is not used. RMIMReader calls V3DataTypeHandler to the same location)*/
String projectName = getSelectedProject().getName();
String structureFolder = "platform:/resource/" + projectName + "/Structures/";
String schemaLocation = structureFolder + "coreschemas/datatypes.xsd";
try
{
URI uri = URI.createURI(schemaLocation);
XSDSchema schema = XSDStructure.getXSDRoot(uri);
if (schema == null) throw new MapperException("Data types schema is null");
}
catch (Exception ex)
{
showMessage("Cannot open V3 data types schema expected at '"
+ schemaLocation + "': " + ex.getMessage());
ex.printStackTrace();
return;
}
// (1a) find the location of the selected MIF file
IFile MIFFile = getSelectedFile();
mifFilePath = MIFFile.getLocation().toString();
System.out.println("MIF file location: " + mifFilePath);
try{
// (1b) if the user chooses a CDA MIF, allow him to choose a template usage file
if (RMIMReader.isCDAMIFName(mifFilePath))
{
String usageFilePath = getTemplateUsageFilePath();
if (usageFilePath.equals("")) throw new MapperException("No Template Usage file supplied");
else
{
templateUsageRoot = XMLUtil.getRootElement(usageFilePath);
/* if the template usage file is new and empty, try to make a default template usage
* file by reading all the templates in sub-folders of the specified folder. */
if (templateUsageRoot.getAttribute("new").equals("yes"))
{
String schematronFolderLocation = templateUsageRoot.getAttribute("schematronFolderLocation");
makeInitialTemplateUsageFile(usageFilePath,schematronFolderLocation);
}
Vector<Element> templateSetEls = XMLUtil.namedChildElements(templateUsageRoot, "templateSet");
if (templateSetEls.size() == 0) throw new MapperException("No template sets in template usage file");
}
}
// (2) Open the RMIM MIF as an XML file
Element MIFRoot = XMLUtil.getRootElement(mifFilePath);
if (MIFRoot == null) throw new MapperException("Cannot opem MIF file at " + mifFilePath);
//the ecore model is stored in the ClassModel folder of the same project as the selected MappedStructure
String ecoreFolderPath = "platform:/resource/" + projectName + "/ClassModel/";
/* (3) Read the MIF file into an ECore class model (and if there are any templates, read them)
* useJavaMappings = true, so there is just one mapping set to invoke the Java mappings.
* The user may optionally choose a schema to order elements */
XSDStructure xsd = FileUtil.userChooseStructure(targetPart);
RMIMReader rmimReader = new RMIMReader(MIFRoot, xsd, mifFilePath, projectName,templateUsageRoot, useJavaMappings, targetPart);
// save the Ecore file, with the same file name root as the MIF file
String filePath = ecoreFolderPath + rmimReader.mifFileRoot() + ".ecore";
rmimReader.writePackage(filePath);
WorkBenchUtil.showMessage("Completed", "Made Ecore model '" + rmimReader.mifFileRoot() + ".ecore' from MIF file");
}
catch (Exception ex)
{
ex.printStackTrace();
showMessage("Failed to convert MIF file to Ecore model: " + ex.getMessage());
}
}
//---------------------------------------------------------------------------------------------------
// HL7 Clinical Document Architecture (CDA) and templates
//---------------------------------------------------------------------------------------------------
/**
* Allow the user to select a template usage file;
* return "" if he cancels.
*/
private String getTemplateUsageFilePath()
{
String title = "Select Template Usage File";
String[] exts = {"*.xml"};
return FileUtil.getFilePathFromUser(targetPart,exts,title,false);
}
/**
* make an initial template usage file, for later editing, by reading all schematrons
* in the sub-folders of a specified folder
* @param usageFilePath file path to the empty template usage file; modify the file name to make the new one
* @param schematronFolderLocation location of the schematron
* @return the root element of the new template usage file
*/
private void makeInitialTemplateUsageFile(String usageFilePath, String schematronFolderLocation)
throws MapperException
{
// make the new template usage file
Document outDoc = XMLUtil.makeOutDoc();
Element collection = XMLUtil.newElement(outDoc, "templateCollection");
collection.setAttribute("schematronFolderLocation", schematronFolderLocation);
collection.setAttribute("name", "blah");
outDoc.appendChild(collection);
// make a dummy template collection
TemplateCollection templateCollection = new TemplateCollection(collection,null);
// look for schematrons in the folder
File folder = new File(schematronFolderLocation);
String[] folders = folder.list();
for (int i = 0; i < folders.length; i++)
{
String subFolderName = folders[i];
StringTokenizer st = new StringTokenizer(subFolderName,".");
if (st.countTokens() == 1)
{
String subFolderPath = schematronFolderLocation + "/" + subFolderName;
File subFolder = new File(subFolderPath);
String[] contents = subFolder.list();
// find the '.sch' file(s) in each sub-folder
for (int j = 0; j < contents.length; j++)
{
if (contents[j].endsWith(".sch"))
{
/* 10/2010: In one folder for C32 there are two .sch files, HandP.IHE.PCC.sch and HandP.sch
* For the moment, I arbitrarily pick the latter. */
if (!contents[j].endsWith(".PCC.sch"))
{
Element setEl = XMLUtil.newElement(outDoc, "templateSet");
setEl.setAttribute("name", ("blah_" + i + j));
setEl.setAttribute("schFileName", contents[j]);
setEl.setAttribute("subfolder", subFolderName);
collection.appendChild(setEl);
Element[] levels = addEmptyLevels(outDoc,setEl);
String schFilePath = subFolderPath + "/" + contents[j];
TemplateSet tempSet = new TemplateSet(templateCollection,schFilePath,setEl);
tempSet.readTemplateFile(schFilePath, true);
for (Enumeration<String> en = tempSet.templates().keys(); en.hasMoreElements();)
{
String key = en.nextElement();
CDATemplate template = tempSet.templates().get(key);
Element tempEl = XMLUtil.newElement(outDoc, "template");
tempEl.setAttribute("id", template.fullTemplateId());
tempEl.setAttribute("name", template.getTrialTitle());
for (Iterator<String> is = template.constrainedTemplateIds().iterator();is.hasNext();)
{
Element con = XMLUtil.textElement(outDoc, "constrains", is.next());
tempEl.appendChild(con);
}
int level = template.heuristicLevel();
levels[level].appendChild(tempEl);
}
}
}
}
}
}
// save the new template usage file
String relativeFilePath = FileUtil.resourceLocation(newUsageFilePath(usageFilePath));
EclipseFileUtil.writeOutputResource(outDoc, relativeFilePath, true);
}
/**
* add empty <templateLevel> elements to the initial template usage file
* @param outDoc
* @param setEl
* @return
* @throws MapperException
*/
private Element[] addEmptyLevels(Document outDoc, Element setEl) throws MapperException
{
Element[] levels = new Element[4];
for (int i = 0; i < 4; i++)
{
levels[i] = XMLUtil.newElement(outDoc, "templateLevel");
levels[i].setAttribute("level", TemplateSet.levelNames[i]);
setEl.appendChild(levels[i]);
}
return levels;
}
/**
* make a slightly different file name for the initial template usage file,
* so it does not overwrite the empty template usage file which caused it to be created
* @param usageFilePath path to the empty template usage file
* @return path to the modified file name
* @throws MapperException
*/
private String newUsageFilePath(String usageFilePath) throws MapperException
{
String newPath = "";
if (usageFilePath.endsWith(".xml"))
{
String path = usageFilePath.substring(0, usageFilePath.length()-4);
newPath = path + "_1" + ".xml";
}
else throw new MapperException("Template usage file name does not end in '.xml'");
return newPath;
}
//----------------------------------------------------------------------------------------------
// Eclipse plumbing
//----------------------------------------------------------------------------------------------
/**
* @return the project in which the selected mif file is located
*/
protected IProject getSelectedProject()
{
if (getSelectedFile() != null) return getSelectedFile().getProject();
return null;
}
@Override
public void selectionChanged(IAction action, ISelection selection) {
this.selection = selection;
}
protected void showMessage(String title, String message) {
MessageDialog.openInformation(
targetPart.getSite().getShell(),
title,
message);
}
/**
* default if you can't be bothered to make up a message title
* @param message
*/
protected void showMessage(String message)
{showMessage("Error",message);}
//cache the target part so we can get the shell
public void setActivePart(IAction action, IWorkbenchPart targetPart) {
this.targetPart = targetPart;
}
/**
* @return the file containing the selected MIF
*/
protected IFile getSelectedFile()
{
if (selection instanceof IStructuredSelection)
{
Object el = ((IStructuredSelection)selection).getFirstElement();
if (el instanceof IFile) return (IFile)el;
}
return null;
}
}