/******************************************************************************* * Copyright (c) 2005 IBM 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.bpel.model.util; import java.io.StringReader; import java.io.StringWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import javax.xml.namespace.QName; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.apache.xerces.dom.AttrImpl; import org.apache.xerces.dom.DocumentImpl; import org.apache.xerces.jaxp.DocumentBuilderFactoryImpl; import org.eclipse.bpel.model.BPELPackage; import org.eclipse.bpel.model.Correlation; import org.eclipse.bpel.model.CorrelationSet; import org.eclipse.bpel.model.CorrelationSets; import org.eclipse.bpel.model.Expression; import org.eclipse.bpel.model.FaultHandler; import org.eclipse.bpel.model.From; import org.eclipse.bpel.model.Import; import org.eclipse.bpel.model.Invoke; import org.eclipse.bpel.model.MessageExchange; import org.eclipse.bpel.model.MessageExchanges; import org.eclipse.bpel.model.OnEvent; import org.eclipse.bpel.model.PartnerLink; import org.eclipse.bpel.model.PartnerLinks; import org.eclipse.bpel.model.Process; import org.eclipse.bpel.model.Scope; import org.eclipse.bpel.model.To; import org.eclipse.bpel.model.adapters.AdapterRegistry; import org.eclipse.bpel.model.adapters.INamespaceMap; import org.eclipse.bpel.model.proxy.MessageProxy; import org.eclipse.bpel.model.proxy.OperationProxy; import org.eclipse.bpel.model.proxy.PortTypeProxy; import org.eclipse.bpel.model.reordering.IExtensibilityElementListHandler; import org.eclipse.bpel.model.resource.BPELResource; import org.eclipse.bpel.model.resource.BPELResourceSetImpl; import org.eclipse.bpel.names.NCNameWordDetector; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.wst.wsdl.ExtensibleElement; import org.eclipse.wst.wsdl.Message; import org.eclipse.wst.wsdl.Operation; import org.eclipse.wst.wsdl.PortType; import org.w3c.dom.Attr; import org.w3c.dom.CDATASection; import org.w3c.dom.DOMException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.xml.sax.InputSource; import com.ibm.wsdl.util.xml.DOM2Writer; import com.ibm.wsdl.util.xml.DOMUtils; /** * * @author (IBM) * @author Michal Chmielewski (michal.chmielewski@oracle.com) * * @date Feb 27, 2007 * */ @SuppressWarnings("nls") public class BPELUtils { /** Namespace attribute */ public static final String ATTR_XMLNS = "xmlns"; //$NON-NLS-1$ /** * Lookup an externally defined object by its QName by using a ref object * (any object from the BPEL EMF model) using a refType and name. * * This is a general query mechanism used by the import resolvers to resolve * anything from the perspective of the BPEL process. * * @param ref * the reference object * @param qname * the QName to resolve. * @param name * the name within the QName to resolve (can be null) * @param refType * the thing to look up (look in WSDLUtil and XSDUtil for the * possible values) * @return the looked up object (or null). */ static public EObject lookup(EObject ref, QName qname, String name, String refType) { Process process = getProcess(ref); if (process == null) { return null; } Iterator<?> it = process.getImports().iterator(); EObject result = null; while (it.hasNext()) { Import imp = (Import) it.next(); // The null and "" problem ... String ns = imp.getNamespace(); if (ns == null) { ns = javax.xml.XMLConstants.DEFAULT_NS_PREFIX; } if (ns.equals(qname.getNamespaceURI()) == false || imp.getLocation() == null) { continue; } ImportResolver[] resolvers = ImportResolverRegistry.INSTANCE .getResolvers(imp.getImportType()); for (int i = 0; i < resolvers.length; i++) { result = resolvers[i].resolve(imp, qname, name, refType); if (result != null) { return result; } } } return result; } /** * Get name namespace associated to the prefix. * * @param eObject * the reference object * @param prefix * the prefix to lookup * @return the namespace associated with the prefix. */ public static String getNamespace(EObject eObject, String prefix) { Map<String, String> prefixNSMap = null; EObject context = eObject; while (context != null) { prefixNSMap = getNamespaceMap(context); String value = prefixNSMap.get(prefix); if (value != null) { return value; } context = context.eContainer(); } return null; } /** * Set the namespace prefix on the EObject passed. The namespace -> prefix * mapping is defined on the object passed. Note that this is different then * getPrefix(), where the prefix mapping is searched via the hierarchy of * the EMF model. * * @param eObject * an object at which level the prefix mapping ought to be set. * @param namespaceURI * namespace URI * @param prefix * the prefix. * @throws RuntimeException * if the prefix is already set. */ @SuppressWarnings("unchecked") public static void setPrefix(EObject eObject, String namespaceURI, String prefix) { INamespaceMap<String, String> prefixNSMap = getNamespaceMap(eObject); if (prefixNSMap.containsKey(prefix)) { throw new RuntimeException("Prefix is already mapped!"); } prefixNSMap.put(prefix, namespaceURI); } /** * Given a starting object from within our EMF model return the prefix * associated with the namespace passed. The object tree is traversed up to * the root to find the associated mapping and first such association is * returned. * * @param eObject * the EMF object to start at. * @param namespace * the XML namespace to query for prefix mapping * @return the prefix name or null of no mapping exists */ public static String getNamespacePrefix(EObject eObject, String namespace) { for (EObject context = eObject; context != null; context = context .eContainer()) { List<String> pfxList = getNamespaceMap(context).getReverse( namespace); if (pfxList.size() > 0) { return pfxList.get(0); } } return null; } /** * Given a starting object from within our EMF model set the prefix * associated with the namespace passed. The object tree is traversed up to * the root to find the associated mapping and first such association is * returned. * * @param eObject * the EMF object to start at. * @param namespace * the XML namespace to query for prefix mapping * @param prefix * the prefix to set * */ public static void setNamespacePrefix(EObject eObject, String namespace, String prefix) { setPrefix(getNearestScopeOrProcess(eObject), namespace, prefix); } /** * @param eObject * @return the namespace map for the given object. */ @SuppressWarnings("unchecked") static public INamespaceMap<String, String> getNamespaceMap(EObject eObject) { if (eObject == null) { throw new NullPointerException( "eObject cannot be null in getNamespaceMap()"); } INamespaceMap<String, String> nsMap = null; // Bug 120110 - this eObject may not have a namespace map, but its // ancestors might, so keep searching until we find one or until // we run out of ancestors. while (nsMap==null && eObject!=null) { nsMap = AdapterRegistry.INSTANCE.adapt( eObject, INamespaceMap.class); if (nsMap==null) eObject = eObject.eContainer(); } if (nsMap == null) { throw new IllegalStateException( "INamespaceMap cannot be attached to an eObject"); } return nsMap; } /** * Return the process object, the root of the EMF BPEL model, from the * reference object passed. * * @param eObj * the reference object. * @return the process object, or null */ public static Process getProcess(Object object) { if (object instanceof EObject) { // check if *this* is already the process object if (object instanceof Process) return (Process)object; EObject cont = ((EObject)object).eContainer(); while (cont != null) { if (cont.eClass() == BPELPackage.eINSTANCE.getProcess()) return (Process)cont; cont = cont.eContainer(); } } return null; } /** * Return the closest Scope or Process objects from the EMF object * hierarchy. We use this code to assign namespace prefix mappings when they * don't exist. * * @param eObject * @return closest scope or process object. */ public static EObject getNearestScopeOrProcess(EObject eObject) { EObject context = eObject; while (context != null) { if (context instanceof Scope) { return context; } if (context instanceof Process) { return context; } context = context.eContainer(); } return null; } /** * Return all global and local namespaces of this context * * @param eObject * reference object * @return the prefix map. */ public static Map<String, String> getAllNamespacesForContext(EObject eObject) { Map<String, String> nsMap = new HashMap<String, String>(); EObject context = eObject; while (context != null) { Map<String, String> localNSMap = getNamespaceMap(context); for (Entry<String, String> entry : localNSMap.entrySet()) { if (!nsMap.containsKey(entry.getKey())) { nsMap.put(entry.getKey(), entry.getValue()); } } context = context.eContainer(); } return nsMap; } /** * Reorder extensibility list. * * @param extensibilityElementListHandlers * @param parent * @return the reordered list. */ @SuppressWarnings("unchecked") public static List<ExtensibleElement> reorderExtensibilityList( List<IExtensibilityElementListHandler> extensibilityElementListHandlers, ExtensibleElement parent) { List<ExtensibleElement> tempExtensibilityElementList = new ArrayList<ExtensibleElement>(); tempExtensibilityElementList.addAll(parent.getExtensibilityElements()); if (extensibilityElementListHandlers.isEmpty() || parent.getExtensibilityElements() == null || parent.getExtensibilityElements().size() <= 1) return tempExtensibilityElementList; for (IExtensibilityElementListHandler element : extensibilityElementListHandlers) { element.orderList(parent, tempExtensibilityElementList); } return tempExtensibilityElementList; } /** * Convert string to a BPEL DOM node. * * @param s * the string * @param bpelResource * the BPEL resource * @return the node */ public static Node convertStringToNode(EObject parent, String s, BPELResource bpelResource) { // Create DOM document DocumentBuilderFactory factory = new DocumentBuilderFactoryImpl(); factory.setNamespaceAware(true); factory.setValidating(false); StringBuilder namespaces = new StringBuilder(); Map<String, String> nsMap = getAllNamespacesForContext(parent); for (Entry<String, String> e : nsMap.entrySet()) { String prefix = e.getKey(); String value = e.getValue(); if (BPELConstants.isBPELNamespace(value)) { continue; } if (prefix != "") { namespaces.append("xmlns:"); namespaces.append(prefix); } else { namespaces.append("xmlns"); } namespaces.append("=\""); namespaces.append(value); namespaces.append("\" "); } String namespaceURI = bpelResource.getNamespaceURI(); if (bpelResource.getOptionUseNSPrefix()) { String prefix = "bpws"; s = "<" + prefix + ":from xmlns:" + prefix + "=\"" + namespaceURI + "\" " + namespaces.toString() + ">" + s + "</" + prefix + ":from>"; } else { s = "<from xmlns=\"" + namespaceURI + "\" " + namespaces.toString() + ">" + s + "</from>"; } try { StringReader sr = new StringReader(s); DocumentBuilder builder = factory.newDocumentBuilder(); InputSource source = new InputSource(sr); source.setEncoding("UTF8"); Document document = builder.parse(source); return document.getDocumentElement(); } catch (Exception e) { return null; } } /** * Convert an element to string. * * @param element * the element * @return string version of the element XML source */ public static String elementToString(Element element) { StringWriter writer = new StringWriter(); DOM2Writer.serializeAsXML(element, writer); return writer.getBuffer().toString(); } /** * Create attribute value. * * @param element * @param attribute * @return QName for the attribute value. */ public static QName createAttributeValue(Element element, String attribute) { String prefixedValue = element.getAttribute(attribute); return createQName(element, prefixedValue); } /** * Create QName * * @param element * the element used as a reference for namespace reference. * @param prefixedValue * the prefixed value (NCName as a QName, that is "foo:bar") * @return the QName created */ public static QName createQName(Element element, String prefixedValue) { int index = prefixedValue.indexOf(':'); String prefix = (index != -1) ? prefixedValue.substring(0, index) : null; String localPart = prefixedValue.substring(index + 1); String namespaceURI = DOMUtils.getNamespaceURIFromPrefix(element, prefix); // namespaceURI may be null. That's okay. // Bugzilla 320654 if (prefix==null) return new QName(namespaceURI, localPart); return new QName(namespaceURI, localPart, prefix); } /** * Return a partner link whose name is partnerLinkName. * * @param eObject * the reference object * @param partnerLinkName * the partner link name. * @return the PartnerLink or null if one does not exist. */ public static PartnerLink getPartnerLink(EObject eObject, String partnerLinkName) { EObject container = eObject; while (container != null) { PartnerLinks partnerLinks = null; if (container instanceof Process) { partnerLinks = ((Process) container).getPartnerLinks(); } else if (container instanceof Scope) { partnerLinks = ((Scope) container).getPartnerLinks(); } if (partnerLinks != null) { for (PartnerLink pl : partnerLinks.getChildren()) { if (pl.getName().equals(partnerLinkName)) { return pl; } } } container = container.eContainer(); } return null; } /** * Return a message exchange whose name is messageExchangeName. * * @param eObject * the reference object * @param messageExchangeName * the message exchange name. * @return the MessageExchange or null if one does not exist. */ public static MessageExchange getMessageExchange(EObject eObject, String messageExchangeName) { EObject container = eObject; while (container != null) { MessageExchanges messageExchanges = null; if (container instanceof Process) { messageExchanges = ((Process) container).getMessageExchanges(); } else if (container instanceof Scope) { messageExchanges = ((Scope) container).getMessageExchanges(); } if (messageExchanges != null) { for (MessageExchange me : messageExchanges.getChildren()) { if (me.getName().equals(messageExchangeName)) { return me; } } } container = container.eContainer(); } return null; } /** * Get the correlation set for an activity. * * @param correlation * the correlation object * @param correlationSetName * the name of the correlation set * @return the correlation set for that activity. */ public static CorrelationSet getCorrelationSetForActivity( Correlation correlation, String correlationSetName) { EObject container = correlation.eContainer(); while (container != null) { CorrelationSets correlationSets = null; if (container instanceof Process) { correlationSets = ((Process) container).getCorrelationSets(); } else if (container instanceof Scope) { correlationSets = ((Scope) container).getCorrelationSets(); } else if (container instanceof OnEvent) { correlationSets = ((OnEvent) container).getCorrelationSets(); } if (correlationSets != null) { for (CorrelationSet correlationSet : correlationSets .getChildren()) { if (correlationSet.getName().equals(correlationSetName)) { return correlationSet; } } } container = container.eContainer(); } return null; } /** * Map default namespace attribute "xmlns" to the empty key "" in the * prefix-to-namespace map. * * @param attrName * attribute name * @return the prefix map key */ public static final String getNSPrefixMapKey(final String attrName) { return ATTR_XMLNS.equals(attrName) ? "" : attrName; } /** * Return a port type proxy for the given URI. * * @param uri * the URI * @param activityElement * the reference element for namespace lookups * @param qnameAttribute * the QName (as string) representing the portType. * @return the port type proxy. */ public static PortType getPortType(URI uri, Element activityElement, String qnameAttribute) { QName qName = createAttributeValue(activityElement, qnameAttribute); PortTypeProxy portType = new PortTypeProxy(uri, qName); return portType; } /** * Return the message proxy for the given URI object. * * @param uri * the URI * @param activityElement * the reference element for namespace lookups * @param qnameAttribute * the QName (as string) representing the message. * @return the message proxy. */ public static Message getMessage(URI uri, Element activityElement, String qnameAttribute) { QName qName = createAttributeValue(activityElement, qnameAttribute); MessageProxy message = new MessageProxy(uri, qName); return message; } /** * Return the operation proxy for the given URI object. * * @param uri * the URI * @param portType * the port type that has this operation. * @param activityElement * the reference element for namespace lookups * @param operationAttribute * the QName (as string) representing the operation. * @return the message proxy. */ public static Operation getOperation(URI uri, PortType portType, Element activityElement, String operationAttribute) { if (!activityElement.hasAttribute(operationAttribute)) return null; String operationSignature = activityElement .getAttribute(operationAttribute); Operation operation = new OperationProxy(uri, portType, operationSignature); return operation; } /** * Tests if <code>node</code> is a DOM {@link Element} with a BPEL * namespace. * * @param node * the node to test * @return true if BPEL element, false otherwise. */ public static final boolean isBPELElement(Node node) { return node != null && node.getNodeType() == Node.ELEMENT_NODE && BPELConstants.isBPELNamespace(node.getNamespaceURI()); } /** * Boolean as XML version string. * * @param b * boolean (true/false) * @return "yes" or "no" */ public static String boolean2XML(Boolean b) { if (b == null || b.equals(Boolean.FALSE)) { return "no"; } return "yes"; } /** * Boolean as XML version string. * * @param b * boolean (true/false) * @return "yes" or "no" */ public static String boolean2XML(boolean b) { if (!b) { return "no"; } return "yes"; } public static boolean xml2boolean(String xml) { return "yes".equals(xml); } /** * Create CData section with the string value indicated. * * @param document * @param value * @return the CData section. */ public static CDATASection createCDATASection(Document document, String value) { return document.createCDATASection(stripExtraCR(value)); } /** * Strip extra carriage returns. * * @param value * @return the "pretty" string */ public static String stripExtraCR(String value) { if (value == null) { return null; } StringBuffer result = new StringBuffer(); char[] chars = value.toCharArray(); for (int i = 0; i < chars.length; i++) { result.append(chars[i]); if (i < chars.length - 1) { if (chars[i] == '\r' && chars[i + 1] == '\n') i++; } } return result.toString(); } /** * This is a slightly hacked resource set that we will be using for to solve * the problem of loading the right resources from URLs that betray no * information on the type of the resource. * * @param resourceSet * * @return the BPELResourceSetImpl that walks around the problem indicated. * */ public static BPELResourceSetImpl slightlyHackedResourceSet( ResourceSet resourceSet) { if (resourceSet instanceof BPELResourceSetImpl) { return (BPELResourceSetImpl) resourceSet; } Map<Object, Object> map = resourceSet.getLoadOptions(); BPELResourceSetImpl result = (BPELResourceSetImpl) map .get(BPELResourceSetImpl.SLIGHTLY_HACKED_KEY); if (result == null) { result = new BPELResourceSetImpl(); map.put(BPELResourceSetImpl.SLIGHTLY_HACKED_KEY, result); } return result; } /** * Return the resource set that we should be using to load "specific" type * of resources. The "slightlyHacked" resource set is kept in the load * options map. * * @param eObj * @return the slightly hacked resource set. * */ public static BPELResourceSetImpl slightlyHackedResourceSet(EObject eObj) { return slightlyHackedResourceSet(eObj.eResource().getResourceSet()); } static Map<String, Locale> string2Locale = new HashMap<String, Locale>(); static Map<Locale, String> locale2String = new HashMap<Locale, String>(); static { StringBuilder sb = new StringBuilder(); for (Locale l : Locale.getAvailableLocales()) { sb.setLength(0); sb.append(l.getLanguage()); if (isEmptyOrWhitespace(l.getCountry()) == false) { sb.append("-").append(l.getCountry()); } String key = sb.toString().toLowerCase(); string2Locale.put(key, l); locale2String.put(l, key); } } /** * Lookup the locale for the key. * * @param key * the key (language + country code) * @return the locale or the default locale. */ public static Locale lookupLocaleFor(String key) { if (key == null) { return Locale.getDefault(); } key = key.replace('_', '-').toLowerCase(); Locale locale = string2Locale.get(key); if (locale == null) { locale = Locale.getDefault(); } return locale; } /** * Lookup the key for the locale. * * @param locale * the locale * @return the key associated with it. */ public static String lookupLocaleKeyFor(Locale locale) { if (locale == null) { return locale2String.get(Locale.getDefault()); } String key = locale2String.get(locale); if (key == null) { key = locale2String.get(Locale.getDefault()); } return key; } /** * 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; } static NCNameWordDetector NCNAME_DETECTOR = new NCNameWordDetector(); /** * Checks for a valid prefix name. * * @param pfx * @return true if valid, false if not. */ public static boolean isValidPrefixName(String pfx) { if (pfx == null) { return false; } if (NCNAME_DETECTOR.isValid(pfx) == false) { return false; } /** Must not start with (x|X)(m|M)(l|L) */ return true; } public static boolean isTransparent(Object parent, Object child) { return (child instanceof FaultHandler && parent instanceof Invoke) || (child instanceof Expression && parent instanceof From) || (child instanceof Expression && parent instanceof To); } public static boolean isActivityNode(Node node) { String name = node.getLocalName(); return isBPELElement(node) && (BPELConstants.ND_INVOKE.equals(name) || BPELConstants.ND_ASSIGN.equals(name) || BPELConstants.ND_WHILE.equals(name) || BPELConstants.ND_REPEAT_UNTIL.equals(name) || BPELConstants.ND_RECEIVE.equals(name) || BPELConstants.ND_REPLY.equals(name) || BPELConstants.ND_THROW.equals(name) || BPELConstants.ND_WAIT.equals(name) || BPELConstants.ND_SEQUENCE.equals(name) || BPELConstants.ND_PICK.equals(name) || BPELConstants.ND_FLOW.equals(name) || BPELConstants.ND_SCOPE.equals(name) || BPELConstants.ND_COMPENSATE.equals(name) || BPELConstants.ND_RETHROW.equals(name) || BPELConstants.ND_EXIT.equals(name) || BPELConstants.ND_EXTENSION_ACTIVITY.equals(name) || BPELConstants.ND_INVOKE.equals(name) || BPELConstants.ND_FOR_EACH.equals(name) || BPELConstants.ND_IF.equals(name) || BPELConstants.ND_VALIDATE.equals(name) || BPELConstants.ND_COMPENSATE_SCOPE.equals(name) || BPELConstants.ND_EMPTY.equals(name) || BPELConstants.ND_OPAQUEACTIVITY.equals(name)); } // TODO: (DU) This is here due to // https://issues.apache.org/jira/browse/XERCESJ-1289 public static void copyInto(Node src, Node dest) throws DOMException { // get node factory Document factory = dest.getOwnerDocument(); boolean domimpl = factory instanceof DocumentImpl; // placement variables Node start = src; Node parent = src; Node place = src; // traverse source tree while (place != null) { // copy this node Node node = null; int type = place.getNodeType(); switch (type) { case Node.CDATA_SECTION_NODE: { node = factory.createCDATASection(place.getNodeValue()); break; } case Node.COMMENT_NODE: { node = factory.createComment(place.getNodeValue()); break; } case Node.ELEMENT_NODE: { Element element = factory.createElement(place.getNodeName()); node = element; NamedNodeMap attrs = place.getAttributes(); int attrCount = attrs.getLength(); for (int i = 0; i < attrCount; i++) { Attr attr = (Attr) attrs.item(i); String attrName = attr.getNodeName(); String attrValue = attr.getNodeValue(); element.setAttribute(attrName, attrValue); if (domimpl && !attr.getSpecified()) { ((AttrImpl) element.getAttributeNode(attrName)) .setSpecified(false); } } break; } case Node.ENTITY_REFERENCE_NODE: { node = factory.createEntityReference(place.getNodeName()); break; } case Node.PROCESSING_INSTRUCTION_NODE: { node = factory.createProcessingInstruction(place.getNodeName(), place.getNodeValue()); break; } case Node.TEXT_NODE: { node = factory.createTextNode(place.getNodeValue()); break; } default: { throw new IllegalArgumentException("can't copy node type, " + type + " (" + place.getNodeName() + ')'); } } dest.appendChild(node); // iterate over children if (place.hasChildNodes()) { parent = place; place = place.getFirstChild(); dest = node; } // advance else if (place != start) { place = place.getNextSibling(); while (place == null && parent != start) { place = parent.getNextSibling(); parent = parent.getParentNode(); dest = dest.getParentNode(); } } else { place = null; } } } // copyInto(Node,Node) // https://issues.jboss.org/browse/JBIDE-8068 public static boolean isAbstractProcess(Process process) { Map<String,String>m = BPELUtils.getAllNamespacesForContext(process); boolean result = m.containsValue(BPELConstants.NAMESPACE_ABSTRACT_2007); return result; } }