This is an automated email from the ASF dual-hosted git repository.

robertlazarski pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/axis-axis2-java-core.git

commit 80f087199c8cd5e2b4600df09e2d8ab9a132223d
Author: Robert Lazarski <[email protected]>
AuthorDate: Sat Feb 14 10:14:04 2026 -1000

    Fix AXIS2-5972: Preserve namespace for xs:attribute ref= in ADB code 
generation
    
    When a schema uses <xs:attribute ref="xmime:contentType"/>, the ADB code
    generator was producing code with a null/empty namespace because the 
resolved
    global attribute's getWireName() lost the ref namespace for attributes with
    inline types. This caused getAttributeValue(null, "contentType") and
    writeAttribute("", "contentType", ...) instead of using the correct
    "http://www.w3.org/2005/05/xmlmime"; namespace URI.
    
    After the recursive processAttribute() call for ref attributes, re-key the
    metainfo mapping from the empty-namespace QName to the correct ref QName 
when
    the namespace was lost during resolution.
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 .../axis2/schema/BeanWriterMetaInfoHolder.java     |  57 ++++++++++-
 .../org/apache/axis2/schema/SchemaCompiler.java    |  18 +++-
 .../schema/SchemaCompilerRefAttributeTest.java     | 112 +++++++++++++++++++++
 3 files changed, 182 insertions(+), 5 deletions(-)

diff --git 
a/modules/adb-codegen/src/org/apache/axis2/schema/BeanWriterMetaInfoHolder.java 
b/modules/adb-codegen/src/org/apache/axis2/schema/BeanWriterMetaInfoHolder.java
index a3a2246a13..5ae560cc22 100644
--- 
a/modules/adb-codegen/src/org/apache/axis2/schema/BeanWriterMetaInfoHolder.java
+++ 
b/modules/adb-codegen/src/org/apache/axis2/schema/BeanWriterMetaInfoHolder.java
@@ -961,7 +961,62 @@ public class BeanWriterMetaInfoHolder {
 
     public QName getRestrictionBaseType() {
         return restrictionBaseType;
-    }    
+    }
+
+    /**
+     * Re-keys all map entries from oldKey to newKey across all internal maps.
+     * This is used when an attribute ref= resolves to a global attribute whose
+     * wireName has lost the original namespace, so we need to re-register
+     * the mapping under the correct QName (AXIS2-5972).
+     *
+     * @param oldKey the QName currently used as key (e.g. with empty 
namespace)
+     * @param newKey the correct QName to use (e.g. with the ref namespace)
+     */
+    public void rekeyMapping(QName oldKey, QName newKey) {
+        if (oldKey.equals(newKey)) {
+            return;
+        }
+        // Re-key elementToJavaClassMap
+        if (elementToJavaClassMap.containsKey(oldKey)) {
+            elementToJavaClassMap.put(newKey, 
elementToJavaClassMap.remove(oldKey));
+        }
+        // Re-key elementToSchemaQNameMap
+        if (elementToSchemaQNameMap.containsKey(oldKey)) {
+            elementToSchemaQNameMap.put(newKey, 
elementToSchemaQNameMap.remove(oldKey));
+        }
+        // Re-key specialTypeFlagMap
+        if (specialTypeFlagMap.containsKey(oldKey)) {
+            specialTypeFlagMap.put(newKey, specialTypeFlagMap.remove(oldKey));
+        }
+        // Re-key qNameMaxOccursCountMap
+        if (qNameMaxOccursCountMap.containsKey(oldKey)) {
+            qNameMaxOccursCountMap.put(newKey, 
qNameMaxOccursCountMap.remove(oldKey));
+        }
+        // Re-key qNameMinOccursCountMap
+        if (qNameMinOccursCountMap.containsKey(oldKey)) {
+            qNameMinOccursCountMap.put(newKey, 
qNameMinOccursCountMap.remove(oldKey));
+        }
+        // Re-key elementQNameToDefulatValueMap
+        if (elementQNameToDefulatValueMap.containsKey(oldKey)) {
+            elementQNameToDefulatValueMap.put(newKey, 
elementQNameToDefulatValueMap.remove(oldKey));
+        }
+        // Re-key nillableQNameList
+        int nillIdx = nillableQNameList.indexOf(oldKey);
+        if (nillIdx >= 0) {
+            nillableQNameList.set(nillIdx, newKey);
+        }
+        // Re-key fixedQNameList
+        int fixedIdx = fixedQNameList.indexOf(oldKey);
+        if (fixedIdx >= 0) {
+            fixedQNameList.set(fixedIdx, newKey);
+        }
+        // Re-key qNameOrderMap (maps Integer -> QName, so replace values)
+        for (Map.Entry<Integer, QName> entry : qNameOrderMap.entrySet()) {
+            if (oldKey.equals(entry.getValue())) {
+                entry.setValue(newKey);
+            }
+        }
+    }
 
     @Override
     public String toString() {
diff --git 
a/modules/adb-codegen/src/org/apache/axis2/schema/SchemaCompiler.java 
b/modules/adb-codegen/src/org/apache/axis2/schema/SchemaCompiler.java
index 6bc917255c..16b96d4dfd 100644
--- a/modules/adb-codegen/src/org/apache/axis2/schema/SchemaCompiler.java
+++ b/modules/adb-codegen/src/org/apache/axis2/schema/SchemaCompiler.java
@@ -1898,22 +1898,32 @@ public class SchemaCompiler {
 
         } else if (att.getRef().getTargetQName() != null) {
 
-            XmlSchema resolvedSchema = getParentSchema(parentSchema, 
att.getRef().getTargetQName(),
+            QName refQName = att.getRef().getTargetQName();
+            XmlSchema resolvedSchema = getParentSchema(parentSchema, refQName,
                                                        COMPONENT_ATTRIBUTE);
             if (resolvedSchema == null) {
                 throw new SchemaCompilationException("can not find the 
attribute " +
-                                                     
att.getRef().getTargetQName() +
+                                                     refQName +
                                                      " from the parent schema 
" +
                                                      
parentSchema.getTargetNamespace());
             } else {
                 XmlSchemaAttribute xmlSchemaAttribute =
-                        
resolvedSchema.getAttributes().get(att.getRef().getTargetQName());
+                        resolvedSchema.getAttributes().get(refQName);
                 if (xmlSchemaAttribute != null) {
                     // call recursively to process the schema
                     processAttribute(xmlSchemaAttribute, metainf, 
resolvedSchema);
+                    // AXIS2-5972: Preserve the namespace from the ref QName.
+                    // The resolved attribute's getWireName() may return an 
empty namespace
+                    // for global attributes with inline types, losing the ref 
namespace.
+                    QName wireName = xmlSchemaAttribute.getWireName();
+                    if (wireName != null
+                            && (wireName.getNamespaceURI() == null || 
wireName.getNamespaceURI().isEmpty())
+                            && refQName.getNamespaceURI() != null && 
!refQName.getNamespaceURI().isEmpty()) {
+                        metainf.rekeyMapping(wireName, refQName);
+                    }
                 } else {
                     throw new SchemaCompilationException("Attribute QName 
reference refer to an invalid attribute " +
-                                                         
att.getRef().getTargetQName());
+                                                         refQName);
                 }
             }
 
diff --git 
a/modules/adb-codegen/test/org/apache/axis2/schema/SchemaCompilerRefAttributeTest.java
 
b/modules/adb-codegen/test/org/apache/axis2/schema/SchemaCompilerRefAttributeTest.java
new file mode 100644
index 0000000000..ceec8e7cd5
--- /dev/null
+++ 
b/modules/adb-codegen/test/org/apache/axis2/schema/SchemaCompilerRefAttributeTest.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.axis2.schema;
+
+import junit.framework.TestCase;
+import org.apache.ws.commons.schema.XmlSchema;
+import org.apache.ws.commons.schema.XmlSchemaCollection;
+import org.w3c.dom.Document;
+
+import javax.xml.namespace.QName;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.io.File;
+import java.lang.reflect.Field;
+import java.util.HashMap;
+
+/**
+ * Test for AXIS2-5972: Verifies that xs:attribute ref= preserves the namespace
+ * from the ref QName in the generated metainfo.
+ */
+public class SchemaCompilerRefAttributeTest extends TestCase {
+
+    private static final String XMLMIME_NS = 
"http://www.w3.org/2005/05/xmlmime";;
+
+    /**
+     * Compiles xmlmime.xsd and verifies that the contentType attribute on the
+     * base64Binary type is registered with the correct namespace URI from the
+     * ref QName, not with an empty namespace.
+     */
+    public void testRefAttributeNamespacePreserved() throws Exception {
+        // Load the xmlmime.xsd schema
+        String basedir = System.getProperty("basedir", ".");
+        File schemaFile = new File(basedir + 
"/test-resources/std/xmlmime.xsd");
+        assertTrue("xmlmime.xsd not found at " + schemaFile.getAbsolutePath(), 
schemaFile.exists());
+
+        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+        dbf.setNamespaceAware(true);
+        DocumentBuilder builder = dbf.newDocumentBuilder();
+        Document doc = builder.parse(schemaFile);
+
+        XmlSchemaCollection schemaCol = new XmlSchemaCollection();
+        XmlSchema schema = schemaCol.read(doc, null);
+
+        // Compile the schema with generateAll=true since xmlmime.xsd has
+        // only types (no elements), and types are only compiled when 
generateAll is set
+        CompilerOptions compilerOptions = new CompilerOptions();
+        compilerOptions.setGenerateAll(true);
+        SchemaCompiler compiler = new SchemaCompiler(compilerOptions);
+        compiler.compile(schema);
+
+        // Access the processedTypeMetaInfoMap via reflection
+        Field metaInfoMapField = 
SchemaCompiler.class.getDeclaredField("processedTypeMetaInfoMap");
+        metaInfoMapField.setAccessible(true);
+        @SuppressWarnings("unchecked")
+        HashMap<QName, BeanWriterMetaInfoHolder> metaInfoMap =
+                (HashMap<QName, BeanWriterMetaInfoHolder>) 
metaInfoMapField.get(compiler);
+
+        // Check the base64Binary type's metainfo
+        QName base64BinaryQName = new QName(XMLMIME_NS, "base64Binary");
+        BeanWriterMetaInfoHolder metainf = metaInfoMap.get(base64BinaryQName);
+        assertNotNull("MetaInfo for base64Binary type should exist", metainf);
+
+        // The contentType attribute should be registered with the xmlmime 
namespace
+        QName expectedAttrQName = new QName(XMLMIME_NS, "contentType");
+        QName wrongAttrQName = new QName("", "contentType");
+
+        // Verify the attribute is registered with the correct namespace
+        String javaClass = metainf.getClassNameForQName(expectedAttrQName);
+        assertNotNull(
+                "contentType attribute should be registered with namespace " + 
XMLMIME_NS +
+                        " (AXIS2-5972: ref attribute namespace must be 
preserved)",
+                javaClass);
+
+        // Verify the attribute is NOT registered with an empty namespace
+        String wrongJavaClass = metainf.getClassNameForQName(wrongAttrQName);
+        assertNull(
+                "contentType attribute should NOT be registered with empty 
namespace",
+                wrongJavaClass);
+
+        // Verify it's flagged as an attribute type
+        assertTrue(
+                "contentType should be flagged as an attribute",
+                metainf.getAttributeStatusForQName(expectedAttrQName));
+
+        // Also check hexBinary type
+        QName hexBinaryQName = new QName(XMLMIME_NS, "hexBinary");
+        BeanWriterMetaInfoHolder hexMetainf = metaInfoMap.get(hexBinaryQName);
+        assertNotNull("MetaInfo for hexBinary type should exist", hexMetainf);
+
+        String hexJavaClass = 
hexMetainf.getClassNameForQName(expectedAttrQName);
+        assertNotNull(
+                "contentType attribute on hexBinary should also have correct 
namespace",
+                hexJavaClass);
+    }
+}

Reply via email to