Fix problems with prefix to namespace mapping

Writing: set default namespace = target namespace.

Reading: correct detection whether a prefix points to the target 
namespace. Allow the default namespace to point to a different
file.

With this commit, all test cases from QNameReferenceTest succeed.

Bug: 329668
diff --git a/org.eclipse.bpmn2/src/org/eclipse/bpmn2/util/Bpmn2ResourceImpl.java b/org.eclipse.bpmn2/src/org/eclipse/bpmn2/util/Bpmn2ResourceImpl.java
index 204b3cc..be51f19 100644
--- a/org.eclipse.bpmn2/src/org/eclipse/bpmn2/util/Bpmn2ResourceImpl.java
+++ b/org.eclipse.bpmn2/src/org/eclipse/bpmn2/util/Bpmn2ResourceImpl.java
@@ -17,6 +17,8 @@
 import java.util.Iterator;

 import java.util.Map;

 

+import javax.xml.XMLConstants;

+

 import org.eclipse.bpmn2.Bpmn2Package;

 import org.eclipse.bpmn2.Definitions;

 import org.eclipse.bpmn2.DocumentRoot;

@@ -203,18 +205,55 @@
         /**

          * Checks if the given prefix is pointing to the current target namespace and thus is optional.

          * The method is called during load.

-         * @param prefix

-         * @return

+         * @param prefix The prefix or null, if no prefix is given (interpreted as default namespace).

+         * @return <code>true</code>, if the namespace associated with the prefix equals the target namespace 

+         * of this Definitions.

+         * If prefix is null or the empty string, then the default namespace is compared with the target namespace.

+         * If prefix is null/empty and default namespace is not defined, <code>true</code> if the target namespace 

+         * is not defined either.

+         * 

+         * <p>

+         * The above rules describe a strict interpretation of the rules for QName resolution. 

+         * This method relaxes these rules and additionally returns <code>true</code> in the following cases:

+         * <ul>

+         * <li>prefix is null/empty, no default namespace (regardless of target namespace)</li>

+         * <li>prefix is null/empty, default namespace is not {@linkplain #findImportForNamespace(String) mapped by an import element}.</li>

+         * </ul>

+         * </p>

          */

         public boolean isTargetNamespace(String prefix) {

+            if (prefix == null)

+                prefix = XMLConstants.DEFAULT_NS_PREFIX;

             final String prefixNs = this.getNamespaceURI(prefix);

 

-            // "prefixNs == null" means: invalid prefix (not declared or xmlns:prefix="").

-            if (prefixNs == null)

+            if (prefixNs == null) {

+                if (XMLConstants.DEFAULT_NS_PREFIX.equals(prefix))

+                    /*

+                     * The (empty) prefix points to {no namespace}, because no default namespace is defined.

+                     * This would be OK if target namespace is undefined as well (meaning {no namespace}).

+                     * 

+                     * However, we employ a relaxed interpretation and do not require that 

+                     *   getDefinitions().getTargetNamespace() == null (i.e. target namespace == {no namespace})

+                     * Every unprefixed QName is interpreted as local reference, if the default namespace is not defined.

+                     */

+                    return true;

+

+                // the non-empty prefix is not mapped to a namespace

                 throw new IllegalArgumentException(String.format("The prefix '%s' is not valid.",

                         prefix));

+            }

 

-            return prefixNs.equals(getDefinitions().getTargetNamespace());

+            // result with strict evaluation: return prefixNs.equals(getDefinitions().getTargetNamespace())

+            if (prefixNs.equals(getDefinitions().getTargetNamespace()))

+                return true;

+            else if (XMLConstants.DEFAULT_NS_PREFIX.equals(prefix)

+                    && findImportForNamespace(prefixNs) == null) {

+                // The default namespace is not mapped to a location by an import element.

+                // Guess that the unprefixed QName should point to a local element (relaxed interpretation)

+                // TODO: emit warning

+                return true;

+            } else

+                return false;

         }

 

         /**

@@ -224,19 +263,33 @@
          * @return

          */

         public String getPathForPrefix(String prefix) {

-            String ns = this.getNamespaceURI(prefix);

+            String ns = this.getNamespaceURI(prefix == null ? XMLConstants.DEFAULT_NS_PREFIX

+                    : prefix);

             if (ns != null) {

-                for (Import imp : getDefinitions().getImports()) {

-                    if (ns.equals(imp.getNamespace())) {

-                        // TODO: Also check that imp.getType() is BPMN

-                        return imp.getLocation();

-                    }

-                }

+                Import imp = findImportForNamespace(ns);

+                if (imp != null)

+                    return imp.getLocation();

             }

             return "";

         }

 

         /**

+         * Looks up the list of import elements in this definitions for an import of the given namespace.

+         * @param namespace The namespace to look for in {@link Import#getNamespace()}.

+         * @return The first import element in {@link Definitions#getImports()} with {@link Import#getNamespace()}

+         * equal to the given namespace.

+         */

+        protected Import findImportForNamespace(String namespace) {

+            for (Import imp : getDefinitions().getImports()) {

+                if (namespace.equals(imp.getNamespace())) {

+                    // TODO: Also check that imp.getType() is BPMN

+                    return imp;

+                }

+            }

+            return null;

+        }

+

+        /**

          * Partly stolen from XmlHelperImpl.setPrefixToNamespaceMap().

          * Ensuring that namespace declaration is saved seems to be really tricky.

          * We will necessarily create a dummy package to ensure that later XmlSaveImpl.addNamespaceDeclarations() writes the ns declaration for us

@@ -252,7 +305,13 @@
                 ePackage = extendedMetaData.demandPackage(namespace);

                 // This will internally create a nice prefix

             }

-            String prefix = ePackage.getNsPrefix();

+

+            String prefix;

+            if (namespace.equals(getDefinitions().getTargetNamespace()))

+                // try to use the default namespace (xmlns="...") for local references

+                prefix = XMLConstants.DEFAULT_NS_PREFIX;

+            else

+                prefix = ePackage.getNsPrefix();

 

             // Make prefix unique

             String originalPrefix = prefix + "_";

@@ -279,11 +338,17 @@
         public String getNsPrefix(String filePath) {

             String ns = null;

             String prefix = "";

-            for (Import imp : getDefinitions().getImports()) {

-                if (filePath.equals(imp.getLocation())) {

-                    // TODO: Also check that imp.getType() is BPMN

-                    ns = imp.getNamespace();

-                    break;

+

+            if (filePath.equals(getResource().getURI().toString())) // TODO: this needs to operate on "canonical paths"

+                // reference to local element

+                ns = getDefinitions().getTargetNamespace();

+            else {

+                for (Import imp : getDefinitions().getImports()) {

+                    if (filePath.equals(imp.getLocation())) {

+                        // TODO: Also check that imp.getType() is BPMN

+                        ns = imp.getNamespace();

+                        break;

+                    }

                 }

             }

             if (ns != null) {

diff --git a/org.eclipse.bpmn2/src/org/eclipse/bpmn2/util/QNameURIHandler.java b/org.eclipse.bpmn2/src/org/eclipse/bpmn2/util/QNameURIHandler.java
index 409c3b2..24430f6 100644
--- a/org.eclipse.bpmn2/src/org/eclipse/bpmn2/util/QNameURIHandler.java
+++ b/org.eclipse.bpmn2/src/org/eclipse/bpmn2/util/QNameURIHandler.java
@@ -62,14 +62,12 @@
             if (fragment.startsWith("/")) { // We have probably found a URL. Better leave it alone

                 return qName;

             }

-

         } else

             throw new IllegalArgumentException("Illegal QName: " + qName);

 

-        if (prefix != null && !xmlHelper.isTargetNamespace(prefix))

+        if (!xmlHelper.isTargetNamespace(prefix))

             return xmlHelper.getPathForPrefix(prefix) + "#" + fragment;

         else

-            // TODO: no prefix does not imply target namespace, but default namespace

             return baseURI.appendFragment(fragment).toString();

     }