Repository: tefra/xsdata

Issue:
xsdata creates two classes for the same type
I am not sure what is going on, but there seems to be two dataclasses defined from the same type.  The same problem shows up in different schemas from here:  http://www.accellera.org/XMLSchema

Specifically the problematic type is portAccessType defined in:

http://www.accellera.org/XMLSchema/SPIRIT/1.5/port.xsd
http://www.accellera.org/XMLSchema/SPIRIT/1685-2009/port.xsd
http://www.accellera.org/XMLSchema/IPXACT/1685-2014/port.xsd
http://www.accellera.org/XMLSchema/IPXACT/1685-2022/port.xsd

They all show the same issue.

I see two python files (`port_access_type.py` and `port_access_type_1.py`) generated with classes `PortAccessType` and `PortAccessType1` respectively.  Both of these have:
```python
class Meta:
    name = "portAccessType"
```
Here are the two files:
***port_access_type.py***
```python
from dataclasses import dataclass, field
from typing import Optional

from org.accellera.ipxact.v1685_2014.simple_port_access_type import (
    SimplePortAccessType,
)

__NAMESPACE__ = "http://www.accellera.org/XMLSchema/IPXACT/1685-2014"


@dataclass(slots=True)
class PortAccessType:
    """Indicates how a netlister accesses a port.

    'ref' means accessed by reference (default) and 'ptr' means accessed
    by pointer.
    """

    class Meta:
        name = "portAccessType"
        namespace = "http://www.accellera.org/XMLSchema/IPXACT/1685-2014"

    value: Optional[SimplePortAccessType] = field(
        default=None,
        metadata={
            "required": True,
        },
    )
```
***port_access_type1.py***
```python
from collections.abc import Iterable
from dataclasses import dataclass, field
from typing import Optional

from org.accellera.ipxact.v1685_2014.leaf_access_handle import LeafAccessHandle
from org.accellera.ipxact.v1685_2014.port_access_type import PortAccessType

__NAMESPACE__ = "http://www.accellera.org/XMLSchema/IPXACT/1685-2014"


@dataclass(slots=True)
class PortAccessType1:
    """
    :ivar port_access_type: Indicates how a netlister accesses a port.
        'ref' means accessed by reference (default) and 'ptr' means
        accessed through a pointer.
    :ivar access_handles:
    """

    class Meta:
        name = "portAccessType"

    port_access_type: Optional[PortAccessType] = field(
        default=None,
        metadata={
            "name": "portAccessType",
            "type": "Element",
            "namespace": "http://www.accellera.org/XMLSchema/IPXACT/1685-2014",
        },
    )
    access_handles: Optional["PortAccessType1.AccessHandles"] = field(
        default=None,
        metadata={
            "name": "accessHandles",
            "type": "Element",
            "namespace": "http://www.accellera.org/XMLSchema/IPXACT/1685-2014",
        },
    )

    @dataclass(slots=True)
    class AccessHandles:
        access_handle: Iterable[LeafAccessHandle] = field(
            default_factory=list,
            metadata={
                "name": "accessHandle",
                "type": "Element",
                "namespace": "http://www.accellera.org/XMLSchema/IPXACT/1685-2014",
                "min_occurs": 1,
            },
        )
```

I see a number of duplicate files with `*1.py`, `*2.py` with the same class Meta names!

http://www.accellera.org/XMLSchema/SPIRIT/1685-2009-VE-1.0/

http://www.accellera.org/XMLSchema

Here are the commands I use to generate the binding classes.

```bash
$ xsdata generate --slots --subscriptable-types --generic-collections --relative-imports --structure-style filenames --package org.accellera.spirit.v1_5 http://www.accellera.org/XMLSchema/SPIRIT/1.5/index.xsd

$ xsdata generate --slots --subscriptable-types --generic-collections --relative-imports --structure-style filenames --package org.accellera.spirit.v1685_2009 http://www.accellera.org/XMLSchema/SPIRIT/1685-2009/index.xsd

$ xsdata generate --slots --subscriptable-types --generic-collections --relative-imports --structure-style filenames --package org.accellera.spirit.v1685_2014 http://www.accellera.org/XMLSchema/SPIRIT/1685-2014/index.xsd

$ xsdata generate --slots --subscriptable-types --generic-collections --relative-imports --structure-style filenames --package org.accellera.spirit.v1685_2022 http://www.accellera.org/XMLSchema/SPIRIT/1685-2022/index.xsd



$ xsdata generate --slots --subscriptable-types --generic-collections --relative-imports --structure-style filenames --package org.accellera.spirit.v1685_2009.ve http://www.accellera.org/XMLSchema/SPIRIT/1685-2009-VE-1.0/index.xsd

$ xsdata generate --slots --subscriptable-types --generic-collections --relative-imports --structure-style filenames --package org.accellera.spirit.v1685_2009.ve.ams http://www.accellera.org/XMLSchema/SPIRIT/1685-2009-VE-1.0/AMS/index.xsd

$ xsdata generate --slots --subscriptable-types --generic-collections --relative-imports --structure-style filenames --package org.accellera.spirit.v1685_2009.ve.core http://www.accellera.org/XMLSchema/SPIRIT/1685-2009-VE-1.0/CORE/index.xsd

$ xsdata generate --slots --subscriptable-types --generic-collections --relative-imports --structure-style filenames --package org.accellera.spirit.v1685_2009.ve.pdp http://www.accellera.org/XMLSchema/SPIRIT/1685-2009-VE-1.0/PDP/index.xsd

$ xsdata generate --slots --subscriptable-types --generic-collections --relative-imports --structure-style filenames --package org.accellera.spirit.v1685_2009.ve.power http://www.accellera.org/XMLSchema/SPIRIT/1685-2009-VE-1.0/POWER/index.xsd
```

I am not sure if the problem is with the XSDs or a bug in xsdata.

Discussion:
sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=tefra_xsdata&pullRequest=1107) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=1107&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=tefra_xsdata&pullRequest=1107&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=tefra_xsdata&pullRequest=1107&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=1107&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=tefra_xsdata&pullRequest=1107&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=tefra_xsdata&pullRequest=1107)


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/tefra/xsdata/pull/1107?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 99.95%. Comparing base [(`474b072`)](https://app.codecov.io/gh/tefra/xsdata/commit/474b072cd05c2f7368ad1680aff7515b9d009f0a?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou) to head [(`16b9c7f`)](https://app.codecov.io/gh/tefra/xsdata/commit/16b9c7fd04af9cc436984ab14c73afaaf3768f81?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou).

<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff             @@
##              main    #1107      +/-   ##
===========================================
- Coverage   100.00%   99.95%   -0.05%     
===========================================
  Files          115      115              
  Lines         9299     9299              
  Branches      1417     1417              
===========================================
- Hits          9299     9295       -4     
- Misses           0        3       +3     
- Partials         0        1       +1     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/tefra/xsdata/pull/1107?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Chris+Tsou).


Patch:
diff --git a/xsdata/codegen/handlers/process_attributes_types.py b/xsdata/codegen/handlers/process_attributes_types.py
index adba61e1..2475be57 100644
--- a/xsdata/codegen/handlers/process_attributes_types.py
+++ b/xsdata/codegen/handlers/process_attributes_types.py
@@ -113,16 +113,19 @@ class ProcessAttributeTypes(RelativeHandlerInterface):
         if attr.restrictions.pattern:
             cls.reset_attribute_type(attr_type)
 
-    def find_dependency(self, attr_type: AttrType, tag: str) -> Optional[Class]:
+    def find_dependency(
+        self, target: Class, attr_type: AttrType, tag: str
+    ) -> Optional[Class]:
         """Find the source type from the attr type and tag.
 
         Avoid conflicts by selecting any matching type by qname and preferably:
-            1. Match the candidate object tag
-            2. Match element again complexType
+            1. Match element again complexType with no self reference
+            2. Match the candidate object tag
             3. Match non element and complexType
             4. Anything
 
         Args:
+            target: The target class instance
             attr_type: The attr type instance
             tag: The xml tag name, e.g. Element, Attribute, ComplexType
 
@@ -130,8 +133,10 @@ class ProcessAttributeTypes(RelativeHandlerInterface):
             The source class or None if no match is found
         """
         conditions = (
+            lambda obj: tag == Tag.ELEMENT
+            and obj.tag == Tag.COMPLEX_TYPE
+            and obj is not target,
             lambda obj: obj.tag == tag,
-            lambda obj: tag == Tag.ELEMENT and obj.tag == Tag.COMPLEX_TYPE,
             lambda obj: not obj.is_complex_type,
             lambda x: True,
         )
@@ -184,7 +189,7 @@ class ProcessAttributeTypes(RelativeHandlerInterface):
             attr: The attr instance
             attr_type: The attr type instance
         """
-        source = self.find_dependency(attr_type, attr.tag)
+        source = self.find_dependency(target, attr_type, attr.tag)
         if not source:
             logger.warning("Reset absent type: %s", attr_type.name)
             self.reset_attribute_type(attr_type)


Test patch:
diff --git a/tests/codegen/handlers/test_process_attributes_types.py b/tests/codegen/handlers/test_process_attributes_types.py
index b7b96178..e207f271 100644
--- a/tests/codegen/handlers/test_process_attributes_types.py
+++ b/tests/codegen/handlers/test_process_attributes_types.py
@@ -344,25 +344,28 @@ class ProcessAttributeTypesTests(FactoryTestCase):
         complex_type = ClassFactory.create(qname="a", tag=Tag.COMPLEX_TYPE)
         simple_type = ClassFactory.create(qname="a", tag=Tag.SIMPLE_TYPE)
 
-        actual = self.processor.find_dependency(attr_type, Tag.ELEMENT)
+        actual = self.processor.find_dependency(element, attr_type, Tag.ELEMENT)
         self.assertIsNone(actual)
 
         self.processor.container.add(simple_type)
-        actual = self.processor.find_dependency(attr_type, Tag.ELEMENT)
+        actual = self.processor.find_dependency(element, attr_type, Tag.ELEMENT)
         self.assertEqual(simple_type, actual)
 
         self.processor.container.add(complex_type)
-        actual = self.processor.find_dependency(attr_type, Tag.ELEMENT)
+        actual = self.processor.find_dependency(element, attr_type, Tag.ELEMENT)
         self.assertEqual(complex_type, actual)
 
         self.processor.container.add(element)
-        actual = self.processor.find_dependency(attr_type, Tag.ELEMENT)
+        actual = self.processor.find_dependency(complex_type, attr_type, Tag.ELEMENT)
         self.assertEqual(element, actual)
 
-        actual = self.processor.find_dependency(attr_type, Tag.SIMPLE_TYPE)
+        actual = self.processor.find_dependency(element, attr_type, Tag.ELEMENT)
+        self.assertEqual(complex_type, actual)
+
+        actual = self.processor.find_dependency(element, attr_type, Tag.SIMPLE_TYPE)
         self.assertEqual(simple_type, actual)
 
-        actual = self.processor.find_dependency(attr_type, Tag.EXTENSION)
+        actual = self.processor.find_dependency(element, attr_type, Tag.EXTENSION)
         self.assertEqual(simple_type, actual)
 
     def test_update_restrictions(self):