[ http://issues.apache.org/jira/browse/XMLBEANS-228?page=comments#action_12361156 ]
Lawrence Jones commented on XMLBEANS-228: ----------------------------------------- Have had a quick look at this. Thanks for the detailed repro - that helps a lot. Here are the things I've discovered so far. You're quite right about the QNameSet problem. I'd recommend a slightly different approach to fixing it. I attached a patch. I can repro the substitution group problem. It arises because underlying every generated XmlBean is an in-memory store in which is kept the underlying XML (in object form). Thus getters actually query this store and setters insert/update data in the store. When you use a setter for the first time what you're actually doing is inserting data into the store. But the solution you propose has a flaw (apart from any performance concerns). This gets prettty complicated and I'm not an expert on this code but I'll do my best to explain (I also attached a schema for which your proposed patch does not work). The code uses what it knows about the schema from which you generated the XmlBean to compute where to insert the data into the store given the elements already inserted. Bear in mind that it must do this even if you are only half way through e.g. calling all the setters, i.e. the current state of the store may not be valid XML for that object (it will be, hopefully, when you are finished calling all the setters etc. but it isn't yet) but you must choose a place to insert the new element anyway. To insert an element via a setter, the way the code works at the moment is to look up in the schema all the elements that must be _after_ the element you are inserting and ensure that the element is inserted just before any of them. (Also if there are any occurences of the element you are inserting in the store already, this new one will go at the end of them.) It does this using the _QName_ of the element you are inserting. Now for a "normal" element there is no problem in defining the elements that "come after" within a given content model. However if you use substitution groups to replace the substitution group head with a replacement element then, as it currently stands, the code doesn't really know where to put that element (it's akin, as far as the store is concerned, to inserting some element that is not part of the content model of that part of the schema at all). The element just gets inserted wherever the cursor is at that point in time. Your suggested solution (as far as I can see) is to not worry about this, but when it comes time to marshal the object, collect all the instances of "things in the same substitution group" together and output them as if they were all in the one place (the place where the substitution group head would be in the schema). Now that's fine if (as in your example schema) you only have one such place. But unfortunately it won't work if there are several places (see e.g. the schema I attached). So I propose a couple of things: 1. Without any code changes, I found that by careful monitoring of the _order_ of setters you can achieve what you want e.g. if you change your example test case to the following then the object successfully marshals and validates: PersonDocument personDocument = PersonDocument.Factory.newInstance(); PersonDocument.Person person = personDocument.addNewPerson(); person.setFirstName("FirstElement"); person.setLastName("SecondElement"); CommentType commentType = person.addNewComment(); QName qName = new QName("urn:www-apache-org:SubstitutionGroup/substitutionGroupInSequence", "FirstCommentElement"); Object resultObject = commentType.substitute(qName, FirstCommentType.type); FirstCommentType firstCommentType = (FirstCommentType) resultObject; firstCommentType.setStringValue("ThirdElement"); person.setComment(firstCommentType); All I've changed is to call the commentType setter after I've set the other 2 types (i.e. in the same position as CommentType comes in the content model). This is not ideal of course. XmlBeans users shouldn't have to worry about the order in which setters are called. However for right now that is (I think) a pretty general solution that will get you around this problem. (You could get even more complicated and explicitly move the XmlCursor to the correct position just before you call the setter which I think would have the same effect). 2. To solve the problem long term (so that users do not have to worry about the order in which they call the setters) I suggest a different approach. When something is inserted into the store which is a substitution for something else the store should keep track of the QNames for _both_ the element we're inserting _and_ the element for which it is substituting (the "original" QName). Then it can use the original name to decide ordering, but when it comes to marshalling (or any query on the store) we output the substituted name. This should allow multiple uses of the same replacement element name in different places in the content model. I have started work on seeing what this means (there may of course be performance concerns on this too). Unfortunately it looks pretty complicated - a large change to the underlying store code - hence this may take some time to get done. > Element order is mixed up on document creation after calling substitute() > ------------------------------------------------------------------------- > > Key: XMLBEANS-228 > URL: http://issues.apache.org/jira/browse/XMLBEANS-228 > Project: XMLBeans > Type: Bug > Components: XmlObject > Versions: Version 2, Version 2.1 > Environment: N/A > Reporter: Andreas Loew > Fix For: Version 2, Version 2.1 > Attachments: SubstitutionGroupTest.java, substGroupTests.xsd, > substitutionGroupFix.patch, substitutionGroupInSequence.xsd, > xmlbeans228_QNameSet.patch > > When trying to create XML documents with XMLBeans 2.x using xmlText() or > save() that use substitution groups, usage of the substitute() method will > mix up the order of the XML document (the order will remain correct when > substitute() will not be called). > We have attached a test case (schema XSD, JUnit test) that demonstrates the > failure and also attached a patch against plain XMLBeans 2.0.0 that shows > where the problem is and what is needed to fix problem (although currently at > the cost of a quite severe perfomance penalty, as I am not an expert in > XMLBeans internals): > For the test case, XMLBeans 2.0.0 as well as the current SVN snapshot both > fail as they return > <?xml version="1.0" encoding="UTF-8"?> > <Person > xmlns="urn:www-apache-org:SubstitutionGroup/substitutionGroupInSequence"> > <FirstCommentElement>ThirdElement</FirstCommentElement> > <FirstName>FirstElement</FirstName> > <LastName>SecondElement</LastName> > </Person> > instead of > <?xml version="1.0" encoding="UTF-8"?> > <Person > xmlns="urn:www-apache-org:SubstitutionGroup/substitutionGroupInSequence"> > <FirstName>FirstElement</FirstName> > <LastName>SecondElement</LastName> > <FirstCommentElement>ThirdElement</FirstCommentElement> > </Person> > as would be correct (and will be returned after applying the patches > provided). > Basically, the main problem is that > XmlObjectBase#getElementEndingDelimiters() after having called > SchemaProperty#getJavaSetterDelimiter(), does not take into account that the > QNameSet returned by getJavaSetterDelimiter() does not yet include the "alias > names" - i.e. possible substitutions - of those elements that happen to be > heads of substitution groups. > The patch for XmlObjectBase adds the missing substitutions to this QNameSet. > This will fix the base problem, but so far at the cost of performance: In > order to simulate a (non-existing) QNameSet iterator on the set of > JavaSetterDelimiters, we have not been able to find a more intelligent > solution than to iterate over the complete array of SchemaGlobalElements and > check each head of a substitution group for inclusion in the QNameSet (using > QNameSet#contains()). This definitely is suboptimal and should be replaced by > an optimized implementation done by somebody who is familiar with the > internals of XMLBeans. (Sorry!) > Unfortunately, while creating the first fix, we ran into a second (small) > issue: > The static method QNameSet#forArray() does mistakenly pass null instead of > new HashSet() to the private constructor, which results in a > NullPointerException. The second fix (also included in the patch) will fix > this problem. > Please get back to me in case you need any additional details. > Thanks & best regards, > Andreas Loew & Patrik Streicher -- This message is automatically generated by JIRA. - If you think it was sent incorrectly contact one of the administrators: http://issues.apache.org/jira/secure/Administrators.jspa - For more information on JIRA, see: http://www.atlassian.com/software/jira --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]