Home » Modeling » EMF » Strange behavior with substitution groups
Strange behavior with substitution groups [message #1077072] |
Thu, 01 August 2013 08:00 |
Heribert Schütz Messages: 24 Registered: July 2009 |
Junior Member |
|
|
Hi,
I am working with XML files whose format I do not control (but I can modify my copy of the schema as long as it remains compatible).
Here's a simplified example:
<?xml version="1.0" encoding="UTF-8"?>
<list>
<itemKindA name="some item of kind A" />
<itemKindB name="some item of kind B" />
<itemKindA name="another item of kind A" />
</list>
The original XML schema uses a choice (with maxOccurs="unbounded") between elements "itemKindA" and "itemKindB". This was translated into a feature map, which didn't work for me because I also want to use the Ecore model for Xtext, which does not work with feature maps.
In some older forum thread I found a link http://ed-merks.blogspot.de/2007/12/winters-icy-grip.html providing a solution for me: substitution groups. So here is my tweaked schema (again largely simplified):
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore"
ecore:package="test.substgroup.example"
ecore:name="example"
ecore:ignoreSubstitutionGroups="true"
>
<xs:element name="list" type="list_t" />
<xs:complexType name="list_t">
<xs:sequence>
<xs:element ref="item" minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
<xs:element name="item" ecore:name="items" type="item_t" abstract="true" />
<xs:complexType name="item_t" abstract="true">
<xs:sequence />
<xs:attribute name="name" type="xs:string" />
</xs:complexType>
<xs:element name="itemKindA" type="itemKindA_t" substitutionGroup="item" />
<xs:complexType name="itemKindA_t">
<xs:complexContent>
<xs:extension base="item_t">
<xs:sequence />
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="itemKindB" type="itemKindB_t" substitutionGroup="item" />
<xs:complexType name="itemKindB_t">
<xs:complexContent>
<xs:extension base="item_t">
<xs:sequence />
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
The generated Ecore model is as I need it. Here is some example Xtend code using it:
package test.substgroup
import java.io.ByteArrayOutputStream
import org.eclipse.emf.common.util.URI
import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.emf.ecore.resource.ResourceSet
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl
import org.eclipse.emf.ecore.xmi.XMLResource
import org.eclipse.emf.ecore.xmi.impl.ElementHandlerImpl
import test.substgroup.example.ExampleFactory
import test.substgroup.example.ExamplePackage
import test.substgroup.example.ListT
import test.substgroup.example.util.ExampleResourceFactoryImpl
class Main {
static boolean doMagicInitialization = false;
def static void main(String[] args) {
val resourceSet = new ResourceSetImpl as ResourceSet => [
resourceFactoryRegistry.extensionToFactoryMap
.put(Resource::Factory::Registry::DEFAULT_EXTENSION, new MyResourceFactoryImpl);
packageRegistry.put(ExamplePackage::eNS_URI, ExamplePackage.eINSTANCE);
if (doMagicInitialization)
createResource(URI::createFileURI("xxx")) => [
contents += ExampleFactory::eINSTANCE.createListT;
save(new ByteArrayOutputStream, null);
]
]
val resource = resourceSet.getResource(URI::createFileURI("data/data.xml"), true);
val list = resource.contents.get(0) as ListT;
for (x: list.items)
println(x);
}
}
class MyResourceFactoryImpl extends ExampleResourceFactoryImpl {
override createResource(URI uri) {
super.createResource(uri) as XMLResource => [
defaultSaveOptions.put(XMLResource.OPTION_ELEMENT_HANDLER, new ElementHandlerImpl(true));
defaultLoadOptions.put(XMLResource.OPTION_SUPPRESS_DOCUMENT_ROOT, true);
]
}
}
If we ignore the "magic initialization" section for now, this code should simply read an XML file like the one given above. But it fails with an exception saying that the feature "itemKindA" was not found.
Strangely enough, some other test cases did work. After a lot of experimentation I found that loading resources worked if some resource was written before. So that's what the "magic initialization" above does. If I switch it on, the example works.
So now my question is: Should I run some (not so hacky) initialization code before loading XML resources?
Did I use EMF wrongly or is this an EMF bug?
Regards,
Heribert.
|
|
|
Re: Strange behavior with substitution groups [message #1077120 is a reply to message #1077072] |
Thu, 01 August 2013 09:11 |
Ed Merks Messages: 33113 Registered: July 2009 |
Senior Member |
|
|
Heribert,
Comments below.
On 01/08/2013 10:00 AM, Heribert Schütz wrote:
> Hi,
>
> I am working with XML files whose format I do not control (but I can
> modify my copy of the schema as long as it remains compatible).
>
> Here's a simplified example:
>
>
> <?xml version="1.0" encoding="UTF-8"?>
> <list>
> <itemKindA name="some item of kind A" />
> <itemKindB name="some item of kind B" />
> <itemKindA name="another item of kind A" />
> </list>
>
>
> The original XML schema uses a choice (with maxOccurs="unbounded")
> between elements "itemKindA" and "itemKindB". This was translated
> into a feature map, which didn't work for me because I also want to
> use the Ecore model for Xtext, which does not work with feature maps.
I see.
>
> In some older forum thread I found a link
> http://ed-merks.blogspot.de/2007/12/winters-icy-grip.html providing a
> solution for me: substitution groups. So here is my tweaked schema
> (again largely simplified):
>
>
> <?xml version="1.0" encoding="UTF-8"?>
> <xs:schema
> xmlns:xs="http://www.w3.org/2001/XMLSchema"
> xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore"
> ecore:package="test.substgroup.example"
> ecore:name="example"
> ecore:ignoreSubstitutionGroups="true"
> >
>
> <xs:element name="list" type="list_t" />
> <xs:complexType name="list_t">
> <xs:sequence>
> <xs:element ref="item" minOccurs="0" maxOccurs="unbounded" />
> </xs:sequence>
> </xs:complexType>
>
> <xs:element name="item" ecore:name="items" type="item_t"
> abstract="true" />
> <xs:complexType name="item_t" abstract="true">
> <xs:sequence />
> <xs:attribute name="name" type="xs:string" />
> </xs:complexType>
>
> <xs:element name="itemKindA" type="itemKindA_t"
> substitutionGroup="item" />
> <xs:complexType name="itemKindA_t">
> <xs:complexContent>
> <xs:extension base="item_t">
> <xs:sequence />
> </xs:extension>
> </xs:complexContent>
> </xs:complexType>
>
> <xs:element name="itemKindB" type="itemKindB_t"
> substitutionGroup="item" />
> <xs:complexType name="itemKindB_t">
> <xs:complexContent>
> <xs:extension base="item_t">
> <xs:sequence />
> </xs:extension>
> </xs:complexContent>
> </xs:complexType>
> </xs:schema>
>
>
> The generated Ecore model is as I need it. Here is some example Xtend
> code using it:
>
>
> package test.substgroup
>
> import java.io.ByteArrayOutputStream
> import org.eclipse.emf.common.util.URI
> import org.eclipse.emf.ecore.resource.Resource
> import org.eclipse.emf.ecore.resource.ResourceSet
> import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl
> import org.eclipse.emf.ecore.xmi.XMLResource
> import org.eclipse.emf.ecore.xmi.impl.ElementHandlerImpl
> import test.substgroup.example.ExampleFactory
> import test.substgroup.example.ExamplePackage
> import test.substgroup.example.ListT
> import test.substgroup.example.util.ExampleResourceFactoryImpl
>
> class Main {
> static boolean doMagicInitialization = false;
>
> def static void main(String[] args) {
> val resourceSet = new ResourceSetImpl as ResourceSet => [
> resourceFactoryRegistry.extensionToFactoryMap
> .put(Resource::Factory::Registry::DEFAULT_EXTENSION, new
> MyResourceFactoryImpl);
> packageRegistry.put(ExamplePackage::eNS_URI,
> ExamplePackage.eINSTANCE);
>
> if (doMagicInitialization)
> createResource(URI::createFileURI("xxx")) => [
What does createResource do?
> contents += ExampleFactory::eINSTANCE.createListT;
> save(new ByteArrayOutputStream, null);
> ]
> ]
>
> val resource =
> resourceSet.getResource(URI::createFileURI("data/data.xml"), true);
>
> val list = resource.contents.get(0) as ListT;
> for (x: list.items)
> println(x);
> }
> }
>
> class MyResourceFactoryImpl extends ExampleResourceFactoryImpl {
> override createResource(URI uri) {
> super.createResource(uri) as XMLResource => [
> defaultSaveOptions.put(XMLResource.OPTION_ELEMENT_HANDLER, new
> ElementHandlerImpl(true));
> defaultLoadOptions.put(XMLResource.OPTION_SUPPRESS_DOCUMENT_ROOT, true);
> ]
> }
> }
>
>
> If we ignore the "magic initialization" section for now, this code
> should simply read an XML file like the one given above. But it fails
> with an exception saying that the feature "itemKindA" was not found.
That sounds like what would happen if your MyResourceFactoryImpl wasn't
actually used to create the resource. You can set a breakpoint to
confirm that...
>
> Strangely enough, some other test cases did work. After a lot of
> experimentation I found that loading resources worked if some resource
> was written before. So that's what the "magic initialization" above
> does. If I switch it on, the example works.
I guess I can't see what all the magic is doing...
>
> So now my question is: Should I run some (not so hacky)
> initialization code before loading XML resources?
No, best we figure out what's wrong.
>
> Did I use EMF wrongly or is this an EMF bug?
Does the generated ExampleExample.java in the generated *.tests project
if you invoke Generate Test Code work correctly?
>
> Regards,
> Heribert.
>
Ed Merks
Professional Support: https://www.macromodeling.com/
|
|
|
Re: Strange behavior with substitution groups [message #1077167 is a reply to message #1077120] |
Thu, 01 August 2013 10:27 |
Heribert Schütz Messages: 24 Registered: July 2009 |
Junior Member |
|
|
Hi Ed,
thanks for your quick reply.
Running ExampleExample.java from the generated test code on my XML example file from the initial post throws the same FeatureNotFoundException that I get with my test code (without the "magic initialization"):
Problem loading file:/home/hs/workspace/test.substgroup.tests/../test.substgroup/data/data.xml
org.eclipse.emf.ecore.resource.impl.ResourceSetImpl$1DiagnosticWrappedException: org.eclipse.emf.ecore.xmi.FeatureNotFoundException: Feature 'itemKindA' not found. (file:/home/hs/workspace/test.substgroup.tests/../test.substgroup/data/data.xml, 3, 43)
at org.eclipse.emf.ecore.resource.impl.ResourceSetImpl.handleDemandLoadException(ResourceSetImpl.java:319)
at org.eclipse.emf.ecore.resource.impl.ResourceSetImpl.demandLoadHelper(ResourceSetImpl.java:278)
at org.eclipse.emf.ecore.resource.impl.ResourceSetImpl.getResource(ResourceSetImpl.java:406)
at test.substgroup.example.tests.ExampleExample.main(ExampleExample.java:88)
Caused by: org.eclipse.emf.ecore.xmi.FeatureNotFoundException: Feature 'itemKindA' not found. (file:/home/hs/workspace/test.substgroup.tests/../test.substgroup/data/data.xml, 3, 43)
at org.eclipse.emf.ecore.xmi.impl.XMLHandler.reportUnknownFeature(XMLHandler.java:1998)
at org.eclipse.emf.ecore.xmi.impl.XMLHandler.handleUnknownFeature(XMLHandler.java:1962)
at org.eclipse.emf.ecore.xmi.impl.XMLHandler.handleFeature(XMLHandler.java:1906)
at org.eclipse.emf.ecore.xmi.impl.XMLHandler.processElement(XMLHandler.java:1030)
at org.eclipse.emf.ecore.xmi.impl.XMLHandler.startElement(XMLHandler.java:1008)
at org.eclipse.emf.ecore.xmi.impl.XMLHandler.startElement(XMLHandler.java:719)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startElement(AbstractSAXParser.java:506)
at com.sun.org.apache.xerces.internal.parsers.AbstractXMLDocumentParser.emptyElement(AbstractXMLDocumentParser.java:182)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanStartElement(XMLDocumentFragmentScannerImpl.java:1303)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2717)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:607)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:489)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:835)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:764)
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:123)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1210)
at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:568)
at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl.parse(SAXParserImpl.java:302)
at org.eclipse.emf.ecore.xmi.impl.XMLLoadImpl.load(XMLLoadImpl.java:175)
at org.eclipse.emf.ecore.xmi.impl.XMLResourceImpl.doLoad(XMLResourceImpl.java:253)
at org.eclipse.emf.ecore.resource.impl.ResourceImpl.load(ResourceImpl.java:1518)
at org.eclipse.emf.ecore.resource.impl.ResourceImpl.load(ResourceImpl.java:1297)
at org.eclipse.emf.ecore.resource.impl.ResourceSetImpl.demandLoad(ResourceSetImpl.java:259)
at org.eclipse.emf.ecore.resource.impl.ResourceSetImpl.demandLoadHelper(ResourceSetImpl.java:274)
... 2 more
According to your suggestion I put a breakpoint in MyResourceFactoryImpl.createResource(...), actually in the line starting with "defaultSaveOptions". Depending on how I invoked my test code it was reached once or twice:
- without "magic initialization" the breakpoint was reached once, from within the call to resourceSet.getResource(...).
- with "magic initialization" the breakpoint was reached twice: From within my call to createResource(URI::createFileURI("xxx")) in the magic code, and a second time as above from resourceSet.getResource(...).
So MyResourceFactoryImpl is being used as expected.
Regarding your question what createResource in the magic code does: It is simply a shortcut for "it.createResource(...)" and "it" is a ResourceSetImpl.
Or did you ask what the entire "magic initialization" does? It uses the resource set to create a resource (with some irrelevant URI). Then it adds a single object to the resource content and finally it writes the resource to some stream (which will be ignored).
Any more ideas?
Regards,
Heribert.
|
|
|
Re: Strange behavior with substitution groups [message #1077201 is a reply to message #1077167] |
Thu, 01 August 2013 11:08 |
Ed Merks Messages: 33113 Registered: July 2009 |
Senior Member |
|
|
Heribert,
Please export the model project and test project to a zip, open a
bugzilla and attach it. Then I can try to reproduce the problem
locally. That's likely faster than trying to guess at the problem from
a description of the symptoms.
On 01/08/2013 12:27 PM, Heribert Schütz wrote:
> Hi Ed,
>
> thanks for your quick reply.
>
> Running ExampleExample.java from the generated test code on my XML
> example file from the initial post throws the same
> FeatureNotFoundException that I get with my test code (without the
> "magic initialization"):
>
>
> Problem loading
> file:/home/hs/workspace/test.substgroup.tests/../test.substgroup/data/data.xml
> org.eclipse.emf.ecore.resource.impl.ResourceSetImpl$1DiagnosticWrappedException:
> org.eclipse.emf.ecore.xmi.FeatureNotFoundException: Feature
> 'itemKindA' not found.
> (file:/home/hs/workspace/test.substgroup.tests/../test.substgroup/data/data.xml,
> 3, 43)
> at
> org.eclipse.emf.ecore.resource.impl.ResourceSetImpl.handleDemandLoadException(ResourceSetImpl.java:319)
> at
> org.eclipse.emf.ecore.resource.impl.ResourceSetImpl.demandLoadHelper(ResourceSetImpl.java:278)
> at
> org.eclipse.emf.ecore.resource.impl.ResourceSetImpl.getResource(ResourceSetImpl.java:406)
> at
> test.substgroup.example.tests.ExampleExample.main(ExampleExample.java:88)
> Caused by: org.eclipse.emf.ecore.xmi.FeatureNotFoundException: Feature
> 'itemKindA' not found.
> (file:/home/hs/workspace/test.substgroup.tests/../test.substgroup/data/data.xml,
> 3, 43)
> at
> org.eclipse.emf.ecore.xmi.impl.XMLHandler.reportUnknownFeature(XMLHandler.java:1998)
> at
> org.eclipse.emf.ecore.xmi.impl.XMLHandler.handleUnknownFeature(XMLHandler.java:1962)
> at
> org.eclipse.emf.ecore.xmi.impl.XMLHandler.handleFeature(XMLHandler.java:1906)
> at
> org.eclipse.emf.ecore.xmi.impl.XMLHandler.processElement(XMLHandler.java:1030)
> at
> org.eclipse.emf.ecore.xmi.impl.XMLHandler.startElement(XMLHandler.java:1008)
> at
> org.eclipse.emf.ecore.xmi.impl.XMLHandler.startElement(XMLHandler.java:719)
> at
> com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startElement(AbstractSAXParser.java:506)
> at
> com.sun.org.apache.xerces.internal.parsers.AbstractXMLDocumentParser.emptyElement(AbstractXMLDocumentParser.java:182)
> at
> com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanStartElement(XMLDocumentFragmentScannerImpl.java:1303)
> at
> com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2717)
> at
> com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:607)
> at
> com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:489)
> at
> com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:835)
> at
> com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:764)
> at
> com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:123)
> at
> com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1210)
> at
> com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:568)
> at
> com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl.parse(SAXParserImpl.java:302)
> at
> org.eclipse.emf.ecore.xmi.impl.XMLLoadImpl.load(XMLLoadImpl.java:175)
> at
> org.eclipse.emf.ecore.xmi.impl.XMLResourceImpl.doLoad(XMLResourceImpl.java:253)
> at
> org.eclipse.emf.ecore.resource.impl.ResourceImpl.load(ResourceImpl.java:1518)
> at
> org.eclipse.emf.ecore.resource.impl.ResourceImpl.load(ResourceImpl.java:1297)
> at
> org.eclipse.emf.ecore.resource.impl.ResourceSetImpl.demandLoad(ResourceSetImpl.java:259)
> at
> org.eclipse.emf.ecore.resource.impl.ResourceSetImpl.demandLoadHelper(ResourceSetImpl.java:274)
> ... 2 more
>
>
> According to your suggestion I put a breakpoint in
> MyResourceFactoryImpl.createResource(...), actually in the line
> starting with "defaultSaveOptions". Depending on how I invoked my
> test code it was reached once or twice:
>
> without "magic initialization" the breakpoint was reached once, from
> within the call to resourceSet.getResource(...).
> with "magic initialization" the breakpoint was reached twice: From
> within my call to createResource(URI::createFileURI("xxx")) in the
> magic code, and a second time as above from resourceSet.getResource(...).
>
> So MyResourceFactoryImpl is being used as expected.
>
> Regarding your question what createResource in the magic code does:
> It is simply a shortcut for "it.createResource(...)" and "it" is a
> ResourceSetImpl.
>
> Or did you ask what the entire "magic initialization" does? It uses
> the resource set to create a resource (with some irrelevant URI).
> Then it adds a single object to the resource content and finally it
> writes the resource to some stream (which will be ignored).
>
> Any more ideas?
>
> Regards,
> Heribert.
>
Ed Merks
Professional Support: https://www.macromodeling.com/
|
|
| | | |
Goto Forum:
Current Time: Fri Mar 29 02:12:14 GMT 2024
Powered by FUDForum. Page generated in 0.04531 seconds
|