package com.openMap1.mapper.writer;
import java.util.Hashtable;
import java.util.List;
import java.util.Iterator;
import java.util.Vector;
import org.eclipse.emf.common.util.URI;
import org.eclipse.core.resources.IFile;
import com.openMap1.mapper.core.CompilationIssue;
import com.openMap1.mapper.core.MapperException;
import com.openMap1.mapper.util.EclipseFileUtil;
import com.openMap1.mapper.util.SystemMessageChannel;
import com.openMap1.mapper.util.messageChannel;
import com.openMap1.mapper.util.FileUtil;
import com.openMap1.mapper.util.XMLUtil;
import com.openMap1.mapper.util.SOAPClient;
import com.openMap1.mapper.MappedStructure;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.Document;
/**
* stub class which compiles a wproc procedure by invoking the
* translation compiler web service
* @author robert
*
*/
public class ProcedureWriterStub implements ProcedureCompiler{
private MappedStructure mappedStructure;
private messageChannel mChan;
Hashtable<String,List<CompilationIssue>> compilationIssues
= new Hashtable<String,List<CompilationIssue>>();
private boolean tracing = false;
public ProcedureWriterStub(MappedStructure mappedStructure, messageChannel mChan)
throws MapperException
{
this.mappedStructure = mappedStructure;
// a message channel can do nothing in the stub, as the messages are written on the server
this.mChan = mChan;
if (this.mChan != null) this.mChan.close(); // to satisfy the compiler
}
//----------------------------------------------------------------------------------------------------
// Compiling a wproc file - either directly, or using the compiler web service
//----------------------------------------------------------------------------------------------------
/*
* One of the two versions of method
* public Element generateProcedures(boolean codeTrace)
* must be made inactive:
*
* (1) to deploy the tools directly calling class ProcedureWriter:
* - a copy of class ProcedureWriter must be in this package com.openMap1.mapper.writer
* - the first (small) version of method generateProcedures must be active
* - the second (long) version must be commented out, or harmlessly renamed (preferred).
*
* (2) to deploy the tools using the compiler web service:
* - there should be no copy of class ProcedureWriter in this package com.openMap1.mapper.writer
* - the first (small) version of method generateProcedures is commented out (to avoid compiler errors)
* - the second (long) version is active.
*/
/**
* direct version - comment out, and reactivate the other version to use the compiler web server
*/
public Element generateProcedures(boolean codeTrace)
throws MapperException
{
trace("starting direct creation wproc file, without using compiler web service");
ProcedureWriter procWriter = new ProcedureWriter(mappedStructure,new SystemMessageChannel());
return procWriter.generateProcedures(codeTrace);
}
/**
* Generate XML writing procedures from a mapper file
* (supplied in the constructor), only if necessary
* (i.e if the mapper file has been edited since the last generate)
* @param codeTrace if true, write out a code trace file (on the server)
* @return the root Element of the procedures file
* @throws MapperException
*/
public Element generateProceduresX(boolean codeTrace)
throws MapperException
{
trace("checking for existing wproc file");
/* if the mapper file has not been edited since the WProc procedures file was made,
* just return the root element of the existing procedures file */
if (!(mappedStructure.hasChangedSinceCompile()))
{
trace("Existing wproc file found");
return mappedStructure.procedureFileRoot();
}
trace("composing SOAP body");
/* the three XML subtrees in the SOAP body are:
* (1) the header, giving email address and key
* (2) the mapping set XML and
* (3) the class model XML */
Element[] request = new Element[3];
/* get a request header with an email and key,
* either by finding them in a file in the plugin storage area,
* or by getting an email address from the user
* (to send with an empty key from the server) */
request[0] = SOAPClient.getEmailAndKey();
if (request[0] == null) throw new MapperException("Translation compile request not completed");
// find the location of the mapping set file on the file system
URI mappingSetURI = mappedStructure.eResource().getURI();
String mappingSetlocation = FileUtil.editURIConverter().normalize(mappingSetURI).toString();
mappingSetlocation = FileUtil.removeFilePrefix(mappingSetlocation); // strip off the prefix 'file:/'
// read the mapping file as XML and find its root
trace("Reading mapping set");
Element mappingRoot = XMLUtil.readXMLFile(mappingSetlocation);
request[1] = mappingRoot;
// find the location of the class Model file on the file system
trace("finding class model");
URI classModelURI = mappedStructure.getClassModelRoot().eResource().getURI();
String location = FileUtil.editURIConverter().normalize(classModelURI).toString();
location = FileUtil.removeFilePrefix(location); // strip off the prefix 'file:/'
// read the class model file as XML and find its root
Element classModelRoot = XMLUtil.readXMLFile(location);
request[2] = classModelRoot;
// put the three elements in a SOAP request, and get the returned XML
trace("sending SOAP compile request");
SOAPClient soapClient = new SOAPClient();
Element[] reply = soapClient.getReply(SOAPClient.COMPILE_REQUEST, request);
trace("handling compiler reply");
Element envelope = reply[0];
if (envelope.getTagName().equals("Envelope"))
{
Element headerEl = XMLUtil.firstNamedChild(envelope, "replyHeader");
if (headerEl == null) throw new MapperException("No header on reply from compiler server");
int replyType = new Integer(headerEl.getAttribute("replyType")).intValue();
// if the server has sent back a new translation service key, store it
String newKey = headerEl.getAttribute("newKey");
if (!newKey.equals("")) SOAPClient.makeKeyFile(SOAPClient.getStoredEmail(), newKey);
// error arose when compiling; throw an exception to display it
if (replyType == SOAPClient.SHOW_ERROR_MESSAGE)
throw new MapperException("Failed to compile " + mappedStructure.getMappingSetName() + ": "
+ headerEl.getAttribute("replyText"));
// successful compilation
else if (replyType == SOAPClient.USE_SUCCESSFUL_COMPILE)
{
trace("successful compile");
Element procFileRoot = XMLUtil.firstNamedChild(envelope, "procedures");
if (procFileRoot != null)
{
readCompilationIssues(procFileRoot);
return procFileRoot;
}
else throw new MapperException("No procedures file returned");
}
else throw new MapperException("Reply type " + replyType + " not recognised");
}
else throw new MapperException("Reply element is not 'envelope' but : " + envelope.getTagName());
}
/**
* Generate XML writing procedures from a mapper file
* (supplied in the constructor).
* If the mapper file has not changed since the last generate, the call
* generateProcedures(codeTrace) will take the lazy option, but we still make
* a new IFile from it (might not be in the usual place)
* @param proceduresFile the IFile to write the result out to
* @param codeTrace if true, write out a code trace file (on the server)
* @throws MapperException
*/
public void generateProcedures(IFile proceduresFile, boolean codeTrace)
throws MapperException
{
/* find the root of the procedures file, using the other API in the interface */
Element procFileRoot = generateProcedures(codeTrace);
// write the document out to the supplied IFile handle
if (procFileRoot != null)
try{
// if there is already a procedures file in the handle, delete it
if (proceduresFile.exists()) proceduresFile.delete(true, null);
Document doc = XMLUtil.makeOutDoc();
Node newRoot = doc.importNode(procFileRoot, true); // true = deep copy
doc.appendChild(newRoot);
// false = not formatted; otherwise you just keep adding more white space to the same files
EclipseFileUtil.writeOutputResource(doc, proceduresFile, false);
}
catch (Exception ex) {throw new MapperException(ex.getMessage());}
else if (procFileRoot == null)
throw new MapperException("No Procedures file handle found");
}
/**
* Compilation issues, noted on the compilation server, have been attached to nodes
* in the wproc file returned. Read them from the XML and store them
* @param procFileRoot
*/
private void readCompilationIssues(Element procFileRoot)
throws MapperException
{
String mappingSetName = FileUtil.getFileName(mappedStructure.eResource().getURI().toString());
compilationIssues = new Hashtable<String,List<CompilationIssue>>();
addCompilationIssues(procFileRoot,compilationIssues,mappingSetName);
}
// recursive descent of the wproc XML structure, collecting compilation issues
private void addCompilationIssues(Element el,
Hashtable<String,List<CompilationIssue>>compilationIssues, String mappingSetName)
throws MapperException
{
// collect all compilation issues on this node
for (Iterator<Element> it = XMLUtil.namedChildElements(el, "CompilationIssue").iterator();it.hasNext();)
{
CompilationIssue ci = new CompilationIssue(it.next());
ci.setFileName(mappingSetName);
String path = ci.pathString();
List<CompilationIssue> lc = compilationIssues.get(path);
if (lc == null) lc = new Vector<CompilationIssue>();
lc.add(ci);
compilationIssues.put(path, lc);
}
// go down through all descendant 'element' nodes, collecting issues
for (Iterator<Element> it = XMLUtil.namedChildElements(el, "element").iterator();it.hasNext();)
addCompilationIssues(it.next(),compilationIssues,mappingSetName);
}
/** Code generation warnings, indexed by XPath to the node involved */
public Hashtable<String,List<CompilationIssue>> getCompilationIssues()
{
return compilationIssues;
}
private void trace(String s) {if (tracing) System.out.println(s);}
}