castor
  1. castor
  2. CASTOR-217

Castor code generator has problems with xml schema groups

    Details

    • Type: Bug Bug
    • Status: Open Open
    • Priority: Major Major
    • Resolution: Unresolved
    • Affects Version/s: 0.9.4
    • Fix Version/s: None
    • Component/s: XML code generator
    • Labels:
      None
    • Environment:
      Operating System: Windows 2000
      Platform: PC
    • Bugzilla Id:
      1072
    • Number of attachments :
      0

      Description

      When I try to generate code based on the IMS Global Learning Consortium content
      packaging schema (http://www.imsproject.org/xsd/imscp_rootv1p1p2.xsd), I get
      problems with the following part of the schema:

      <xsd:group name="grp.any">
      <xsd:annotation>
      <xsd:documentation>Any namespaced element from any namespace may be inc
      luded within an "any" element. The namespace for the imported element
      must be defined in the instance, and the schema must be imported. </xsd:docume
      ntation>
      </xsd:annotation>
      <xsd:sequence>
      <xsd:any namespace="##other" processContents="strict" minOccurs="0" max
      Occurs="unbounded"/>
      </xsd:sequence>
      </xsd:group>

      When I try and unmarshall a document, the resulting exception is:

      Unmarshalling failed: ValidationException: error-if-this-is-used is a
      required field.; - location of error: XPATH:
      manifest/organizations/organization/item at
      org.exolab.castor.xml.FieldValidator.validate(Unknown Source) at
      org.exolab.castor.xml.util.XMLClassDescriptorImpl.validate(Unknown Source) at
      org.exolab.castor.xml.util.XMLClassDescriptorImpl.validate(Unknown Source) at
      org.exolab.castor.xml.Validator.validate(Unknown Source) at
      org.exolab.castor.xml.FieldValidator.validate(Unknown Source) at
      org.exolab.castor.xml.util.XMLClassDescriptorImpl.validate(Unknown Source) at
      org.exolab.castor.xml.util.XMLClassDescriptorImpl.validate(Unknown Source) at
      org.exolab.castor.xml.Validator.validate(Unknown Source) at
      org.exolab.castor.xml.FieldValidator.validate(Unknown Source) at
      org.exolab.castor.xml.util.XMLClassDescriptorImpl.validate(Unknown Source) at
      org.exolab.castor.xml.util.XMLClassDescriptorImpl.validate(Unknown Source) at
      org.exolab.castor.xml.Validator.validate(Unknown Source) at
      org.exolab.castor.xml.FieldValidator.validate(Unknown Source) at
      org.exolab.castor.xml.util.XMLClassDescriptorImpl.validate(Unknown Source) at
      org.exolab.castor.xml.util.XMLClassDescriptorImpl.validate(Unknown Source) at
      org.exolab.castor.xml.Validator.validate(Unknown Source) at
      org.exolab.castor.xml.UnmarshalHandler.endElement(Unknown Source) at
      org.apache.xerces.parsers.SAXParser.endElement(SAXParser.java:1392) at
      org.apache.xerces.validators.common.XMLValidator.callEndElement
      (XMLValidator.java:1550) at
      org.apache.xerces.framework.XMLDocumentScanner$ContentDispatcher.dispatch
      (XMLDocumentScanner.java:1149) at
      org.apache.xerces.framework.XMLDocumentScanner.parseSome
      (XMLDocumentScanner.java:381) at org.apache.xerces.framework.XMLParser.parse
      (XMLParser.java:1098) at org.exolab.castor.xml.Unmarshaller.unmarshal(Unknown
      Source) at org.exolab.castor.xml.Unmarshaller.unmarshal(Unknown Source) at
      org.exolab.castor.xml.Unmarshaller.unmarshal(Unknown Source) at
      com.sun.elearning.lms_.upload_.schemabinding.imscp.Manifest.unmarshal

      It seems that castor generates a 'dummy' XMLFieldDescriptor instance for the
      group, with the name 'error-if-this-is-used'. Unfortunately, the
      XMLFieldDescriptor has it's 'required' attribute set to true, so during
      validation, castor wants there to be an element of that name and complains.

      I've tried to fix my own build of castor by modifying the
      org.exolab.castor.xml.schema.Group class to set it's 'minOccurs' attribute to
      0, rather than the default of 1. This prevents the 'required' attribute from
      being set and everything seems to be fine, but I'm not sure if this is really a
      valid fix.

      This problem appears to be generation time only - the code from my modified
      generator works fine with the normal version of castor.

        Activity

        Hide
        Arnaud Blandin added a comment -

        accept bug

        Show
        Arnaud Blandin added a comment - accept bug
        Hide
        simon.bates added a comment -

        I am also using the IMS Content Packaging schema and experiencing the same
        problem outlined above. I would like to use Castor and would have some time to
        look into fixing this issue.

        I am new to Castor and unfamiliar with the code and I was wondering if Arnaud
        could give me some pointers as to where I should start and what might be going
        wrong?

        Thank you very much for your help and for Castor in general! It is a great
        piece of work.

        Show
        simon.bates added a comment - I am also using the IMS Content Packaging schema and experiencing the same problem outlined above. I would like to use Castor and would have some time to look into fixing this issue. I am new to Castor and unfamiliar with the code and I was wondering if Arnaud could give me some pointers as to where I should start and what might be going wrong? Thank you very much for your help and for Castor in general! It is a great piece of work.
        Hide
        simon.bates added a comment -

        Is Jack's suggestion of modifying org.exolab.castor.xml.schema.Group to set it's
        'minOccurs' attribute to 0 the correct fix?

        Thanks!

        Show
        simon.bates added a comment - Is Jack's suggestion of modifying org.exolab.castor.xml.schema.Group to set it's 'minOccurs' attribute to 0 the correct fix? Thanks!
        Hide
        Arnaud Blandin added a comment -

        The proposed fix is incorrect, you cannot set the value to 0 it is only a
        workaround.
        The main problem comes from the XML Schema design:
        <xsd:group name="grp.any">
        <xsd:sequence>
        <xsd:any namespace="##other" processContents="strict" minOccurs="0"
        maxOccurs="unbounded"/>
        </xsd:sequence>
        </xsd:group>
        is a group that can be empty
        <xsd:complexType name="metadataType">
        <xsd:sequence>
        <xsd:group ref="grp.any"/> XXXXXXX
        </xsd:sequence>
        </xsd:complexType>

        XXXX refers to a group that must appear once (minOccurs=1 by default) but it
        can be empty by the definition of the group.

        This is very bad designed since it is equivalent in OO performance-term that an
        Object will hold a reference to an object that can be empty but not null.
        I would advise as a workaround to refer to the group as:
        <xsd:group ref="grp.any" minOccurs="0"/>

        However Castor shouldn't throw an exception when reading valid document but I
        am still unclear about what we should do:

        1- detect that the group is required
        2- detect that the group itself can be empty
        3- instantiate a EMPTY vector of AnyNode
        ...not sure if it is a good solution

        Show
        Arnaud Blandin added a comment - The proposed fix is incorrect, you cannot set the value to 0 it is only a workaround. The main problem comes from the XML Schema design: <xsd:group name="grp.any"> <xsd:sequence> <xsd:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="unbounded"/> </xsd:sequence> </xsd:group> is a group that can be empty <xsd:complexType name="metadataType"> <xsd:sequence> <xsd:group ref="grp.any"/> XXXXXXX </xsd:sequence> </xsd:complexType> XXXX refers to a group that must appear once (minOccurs=1 by default) but it can be empty by the definition of the group. This is very bad designed since it is equivalent in OO performance-term that an Object will hold a reference to an object that can be empty but not null. I would advise as a workaround to refer to the group as: <xsd:group ref="grp.any" minOccurs="0"/> However Castor shouldn't throw an exception when reading valid document but I am still unclear about what we should do: 1- detect that the group is required 2- detect that the group itself can be empty 3- instantiate a EMPTY vector of AnyNode ...not sure if it is a good solution
        Hide
        simon.bates added a comment -

        Created an attachment (id=233)
        Grp_any.java generated for <xsd:group name="grp.any">

        Show
        simon.bates added a comment - Created an attachment (id=233) Grp_any.java generated for <xsd:group name="grp.any">
        Hide
        simon.bates added a comment -

        Created an attachment (id=234)
        Grp_anyDescriptor.java generated for <xsd:group name="grp.any">

        Show
        simon.bates added a comment - Created an attachment (id=234) Grp_anyDescriptor.java generated for <xsd:group name="grp.any">
        Hide
        simon.bates added a comment -

        Arnaud,

        Thank you very much for your response.

        I think the solution that would match the schema most closely to my mind would
        be that an instance of Grp_any is created at unmarshalling and that
        Grp_any.getAnyObjectCount() returns 0. For example:

        Manifest manifest = Manifest.unmarshal(someReader);
        Metadata metadata = manifest.getMetadata();
        Grp_any a = metadata.getGrp_any();

        for the following XML:

        <manifest>
        <metadata>
        </metadata>
        </manifest>

        a.getAnyObjectCount() would return 0.

        I would be happy to have a go at implementing this is you think it is the
        correct approach and if you could maybe give me a pointer to how this might be done.

        Regards,
        Simon.

        Show
        simon.bates added a comment - Arnaud, Thank you very much for your response. I think the solution that would match the schema most closely to my mind would be that an instance of Grp_any is created at unmarshalling and that Grp_any.getAnyObjectCount() returns 0. For example: Manifest manifest = Manifest.unmarshal(someReader); Metadata metadata = manifest.getMetadata(); Grp_any a = metadata.getGrp_any(); for the following XML: <manifest> <metadata> </metadata> </manifest> a.getAnyObjectCount() would return 0. I would be happy to have a go at implementing this is you think it is the correct approach and if you could maybe give me a pointer to how this might be done. Regards, Simon.
        Hide
        simon.bates added a comment -

        Created an attachment (id=235)
        Descriptor generated for metadataType that references a grp.any group

        Show
        simon.bates added a comment - Created an attachment (id=235) Descriptor generated for metadataType that references a grp.any group
        Hide
        simon.bates added a comment -

        Is a possible solution to modify the source code generator so that:

        In the case that a class contains a group with a minOccurs of 1 and that group
        itself can be empty
        then
        the containing class creates an instance of the class representing the group in
        its constructor?

        For example rather than:

        public ManifestType()

        { super(); _manifestList = new Vector(); }

        could we generate:

        public ManifestType()

        { super(); _manifestList = new Vector(); _grp_any = new ca.utoronto.atrc.tile.imscp.Grp_any(); }

        and if so is this a good solution?

        I started looking into this. It looks like the "_manifestList = new Vector();"
        line is added by
        org.exolab.castor.builder.CollectionInfo.generateInitializerCode which is called
        by org.exolab.castor.builder.SourceFactory.handleField

        Is it feasible to modify the source generator so that SourceFactory.handleField
        adds a line to the constructor that creates a instance of a group as for
        _manifestList above when it is called on a group with minOccurs=1 that can be empty?

        Thanks a lot.

        Show
        simon.bates added a comment - Is a possible solution to modify the source code generator so that: In the case that a class contains a group with a minOccurs of 1 and that group itself can be empty then the containing class creates an instance of the class representing the group in its constructor? For example rather than: public ManifestType() { super(); _manifestList = new Vector(); } could we generate: public ManifestType() { super(); _manifestList = new Vector(); _grp_any = new ca.utoronto.atrc.tile.imscp.Grp_any(); } and if so is this a good solution? I started looking into this. It looks like the "_manifestList = new Vector();" line is added by org.exolab.castor.builder.CollectionInfo.generateInitializerCode which is called by org.exolab.castor.builder.SourceFactory.handleField Is it feasible to modify the source generator so that SourceFactory.handleField adds a line to the constructor that creates a instance of a group as for _manifestList above when it is called on a group with minOccurs=1 that can be empty? Thanks a lot.
        Hide
        simon.bates added a comment -

        Created an attachment (id=245)
        ManifestType.java that contains a Grp_any

        Show
        simon.bates added a comment - Created an attachment (id=245) ManifestType.java that contains a Grp_any

          People

          • Assignee:
            Unassigned
            Reporter:
            Jack Honeghan-Bates
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated: