/******************************************************************************* * Copyright (c) 2006 Oracle Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Oracle Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.bpel.fnmeta.model.util; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.eclipse.bpel.common.extension.model.Activator; import org.eclipse.bpel.fnmeta.model.Argument; import org.eclipse.bpel.fnmeta.model.Assistant; import org.eclipse.bpel.fnmeta.model.FMFactory; import org.eclipse.bpel.fnmeta.model.Function; import org.eclipse.bpel.fnmeta.model.Option; import org.eclipse.bpel.fnmeta.model.Optionality; import org.eclipse.bpel.fnmeta.model.Registry; import org.eclipse.bpel.fnmeta.model.proxy.ArgumentProxy; import org.eclipse.bpel.fnmeta.model.proxy.AssistantProxy; import org.eclipse.core.runtime.IStatus; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.xmi.XMLDefaultHandler; import org.eclipse.emf.ecore.xmi.XMLLoad; import org.eclipse.emf.ecore.xmi.XMLResource; import org.w3c.dom.CDATASection; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Text; import org.xml.sax.ErrorHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; /** * @author Michal Chmielewski (michal.chmielewski@oracle.com) * @date Aug 3, 2007 * */ @SuppressWarnings("nls") public class FMReader implements XMLLoad , ErrorHandler { List<Runnable> fPass2Runnables = new ArrayList<Runnable>(); DocumentBuilder fBuilder; XMLResource fResource; /** * @see org.eclipse.emf.ecore.xmi.XMLLoad#createDefaultHandler() */ public XMLDefaultHandler createDefaultHandler() { return null; } /** * @see org.eclipse.emf.ecore.xmi.XMLLoad#load(org.eclipse.emf.ecore.xmi.XMLResource, java.io.InputStream, java.util.Map) */ public void load (XMLResource resource, InputStream inputStream, Map<?, ?> options) { InputSource inputSource = new InputSource(inputStream); inputSource.setPublicId( resource.getURI().toString() ); inputSource.setSystemId( resource.getURI().toString() ); load (resource,inputSource,options); } /** * @see org.eclipse.emf.ecore.xmi.XMLLoad#load(org.eclipse.emf.ecore.xmi.XMLResource, org.xml.sax.InputSource, java.util.Map) */ public void load (XMLResource resource, InputSource inputSource, Map<?, ?> options) { Element top = null; try { top = read ( inputSource ); // After the document has successfully parsed, it's okay // to assign the resource. } catch (SAXException sax) { // the error handlers will catch this. } catch (IOException ioe) { Activator.log("I/O Error Reading BPEL XML", ioe , IStatus.ERROR) ; } finally { } if (top == null) { return ; } load(resource,top,options); } /** * @see org.eclipse.emf.ecore.xmi.XMLLoad#load(org.eclipse.emf.ecore.xmi.XMLResource, org.w3c.dom.Node, java.util.Map) */ public void load (XMLResource resource, Node node, Map<?, ?> options) { if (node instanceof Element == false) { return ; } fResource = resource; EObject root = pass1( (Element) node); if (root != null) { resource.getContents().add(root); } pass2(); } Element read ( InputSource inputSource ) throws IOException, SAXException { if (fBuilder == null) { fBuilder = getDocumentBuilder(); fBuilder.setErrorHandler(this); } return fBuilder.parse(inputSource).getDocumentElement(); } protected DocumentBuilder getDocumentBuilder() throws IOException { final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setIgnoringComments(true); factory.setIgnoringElementContentWhitespace(true); factory.setValidating(false); factory.setNamespaceAware(true); DocumentBuilder builder; try { builder = factory.newDocumentBuilder(); } catch (ParserConfigurationException exc) { throw new IOException(exc.toString()); } return builder; } /** * In pass 1, we parse and create the structural elements and attributes, * and add the process to the EMF resource's contents * @param document the DOM document to parse */ protected EObject pass1 (Element elm) { return xml2Resource(elm); } /** * In pass 2, we run any post load runn ables which were queued during pass 1. */ protected void pass2() { for(Runnable r : fPass2Runnables) { r.run(); } fPass2Runnables.clear(); } /** * Returns a list of child nodes of <code>parentElement</code> that are * {@link Element}s. * Returns an empty list if no elements are found. * * @param parentElement the element to find the children of * @return a node list of the children of parentElement */ protected List<Element> getChildElements (Element parentElement) { List<Element> list = new ArrayList<Element>(); NodeList children = parentElement.getChildNodes(); for (int i=0; i < children.getLength(); i++) { if (children.item(i).getNodeType() == Node.ELEMENT_NODE) list.add( (Element) children.item(i)); } return list; } /** * * @param parentElement the element to find the children of * @param localName the localName to match against * @return a node list of the matching children of parentElement */ protected List<Element> getChildElementsByLocalName(Element parentElement, String localName) { List<Element> list = new ArrayList<Element>(); for(Node node = parentElement.getFirstChild(); node != null ; node = node.getNextSibling()) { if (localName.equals(node.getLocalName())) { list.add((Element) node); } } return list; } /** * Returns the first child node of <code>parentElement</code> that is an {@link Element} * with a BPEL namespace and the given <code>localName</code>, or <code>null</code> * if a matching element is not found. * * @param parentElement the element to find the children of * @param localName the localName to match against * @return the first matching element, or null if no element was found */ protected Element getChildElementByLocalName (Element parentElement, String localName) { for(Node node = parentElement.getFirstChild(); node != null ; node = node.getNextSibling()) { if (localName.equals(node.getLocalName()) ) { return (Element) node; } } return null; } /** * Converts an XML document to a BPEL Resource object. */ protected EObject xml2Resource(Element element) { return xml2Registry (element); } /** * Converts an XML process to a BPEL Process object. */ @SuppressWarnings("nls") protected Registry xml2Registry(Element element) { if (element == null || element.getLocalName().equals("bpel-xpath-functions") == false ) { return null; } Registry registry = FMFactory.eINSTANCE.createRegistry(); registry.setElement(element); // Assistants for(Element e : getChildElementsByLocalName(element, "assistant")) { registry.getAssistants().add(xml2Assistant(e)); } // Arguments for(Element e : getChildElementsByLocalName(element, "arg")) { registry.getArguments().add(xml2Argument(e)); } // Functions for(Element e : getChildElementsByLocalName(element, "function")) { registry.getFunctions().add(xml2Function(e)); } return registry; } /** * Converts an XML partnerLinks */ protected Assistant xml2Assistant (Element element) { if (element == null || element.getLocalName().equals("assistant") == false) { return null; } if (element.getFirstChild() == null || element.hasAttribute("ref") ) { return new AssistantProxy( fResource.getURI(), element.getAttribute("ref") ); } Assistant assistant = FMFactory.eINSTANCE.createAssistant(); for(Element e : getChildElementsByLocalName(element, "option")) { assistant.getOptions().add( xml2Option(e) ); } return assistant; } protected Option xml2Option (Element element) { if (element == null || element.getLocalName().equals("option") == false) { return null; } Option option = FMFactory.eINSTANCE.createOption(); option.setValue( element.getAttribute("value") ); option.setDisplayValue( getText(element) ); return option; } @SuppressWarnings("boxing") protected Argument xml2Argument (Element element) { if (element == null || element.getLocalName().equals("arg") == false) { return null; } if (element.getFirstChild() == null || element.hasAttribute("ref") ) { return new ArgumentProxy(fResource.getURI(), element.getAttribute("ref") ); } Argument arg = FMFactory.eINSTANCE.createArgument(); arg.setElement(element); arg.setName( element.getAttribute("name") ); arg.setType (element.getAttribute("type") ); arg.setDefaultValue(element.getAttribute("default") ); String opt = element.getAttribute("optional"); /** By default, optionality is Optionality.REQUIRED */ if ("true".equalsIgnoreCase(opt) || "?".equals(opt)) { arg.setOptionality(Optionality.OPTIONAL); } else if ("*".equals(opt)) { arg.setOptionality(Optionality.OPTIONAL_MANY); } arg.setAssistant( xml2Assistant( getChildElementByLocalName(element, "assistant"))); arg.setComment( getText( getChildElementByLocalName(element, "comment"))); return arg; } @SuppressWarnings("boxing") protected Function xml2Function (Element element) { if (element == null || element.getLocalName().equals("function") == false) { return null; } Function function = FMFactory.eINSTANCE.createFunction(); function.setElement(element); function.setName( element.getAttribute("id")); function.setReturnType(element.getAttribute("returns")); function.setHelp( getText( getChildElementByLocalName(element, "help")) ); function.setComment( getText( getChildElementByLocalName(element, "comment")) ); function.setClassName( element.getAttribute("class")); Element dep = getChildElementByLocalName(element,"deprecated"); if (dep != null) { function.setIsDeprecated( false ); function.setDeprecateComment(getText(dep)); } Element ns = getChildElementByLocalName(element, "namespace"); if (ns != null) { function.setNamespace( getText(ns) ); function.setPrefix(ns.getAttribute("prefix")); } for(Element a : getChildElementsByLocalName(element, "arg")) { function.getArguments().add( xml2Argument(a)); } return function; } /** * Returns true if the string is either null or contains just whitespace. * @param value * @return true if empty or whitespace, false otherwise. */ static public boolean isEmptyOrWhitespace( String value ) { if( value == null || value.length() == 0) { return true; } for( int i = 0, j = value.length(); i < j; i++ ) { if( ! Character.isWhitespace( value.charAt(i) ) ) { return false; } } return true; } /** * Returns the text of the given node. If the node is an element node, its * children text value is returned. Otherwise, the node is assumed to be * the first child node and the siblings sequence is scanned. * * */ String getText (Node node) { StringBuilder sb = new StringBuilder(128); if (node instanceof Element) { node = ((Element)node).getFirstChild(); } boolean bCData = false; while (node != null) { switch (node.getNodeType()) { case Node.TEXT_NODE : if (bCData) { break; } Text text = (Text) node; sb.append(text.getData()); break; case Node.CDATA_SECTION_NODE : if (bCData == false) { sb.setLength(0); bCData = true; } CDATASection cdata = (CDATASection) node; sb.append( cdata.getData() ); break; } node = node.getNextSibling(); } String data = sb.toString(); if (isEmptyOrWhitespace(data)) { return null; } return data; } /** * @see org.xml.sax.ErrorHandler#error(org.xml.sax.SAXParseException) */ @SuppressWarnings("boxing") public void error (SAXParseException exception) { String message = java.text.MessageFormat.format( "Error in {0} [{2}:{3}] {4}", exception.getPublicId(), exception.getSystemId(), exception.getLineNumber(), exception.getColumnNumber(), exception.getLocalizedMessage() ); Activator.log(message, exception, IStatus.ERROR); } /** * @see org.xml.sax.ErrorHandler#fatalError(org.xml.sax.SAXParseException) */ @SuppressWarnings("boxing") public void fatalError(SAXParseException exception) { String message = java.text.MessageFormat.format( "Fatal Error in {0} [{2}:{3}] {4}", exception.getPublicId(), exception.getSystemId(), exception.getLineNumber(), exception.getColumnNumber(), exception.getLocalizedMessage() ); Activator.log(message, exception, IStatus.ERROR); } /** * @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException) */ @SuppressWarnings("boxing") public void warning (SAXParseException exception) { String message = java.text.MessageFormat.format( "Warning in {0} [{2}:{3}] {4}", exception.getPublicId(), exception.getSystemId(), exception.getLineNumber(), exception.getColumnNumber(), exception.getLocalizedMessage() ); Activator.log(message, exception, IStatus.WARNING); } }