diff options
author | Andreas Mayer | 2016-05-17 15:19:19 +0000 |
---|---|---|
committer | Laurent Delaigue | 2016-05-20 10:04:35 +0000 |
commit | cc727ea40711b293accea0337cfc32272151cd36 (patch) | |
tree | ea92075836acef224e1984b4d90d745ae44bd92f | |
parent | 7517bb7436d01391643d96096946959f4e72bba4 (diff) | |
download | org.eclipse.emf.compare-cc727ea40711b293accea0337cfc32272151cd36.tar.gz org.eclipse.emf.compare-cc727ea40711b293accea0337cfc32272151cd36.tar.xz org.eclipse.emf.compare-cc727ea40711b293accea0337cfc32272151cd36.zip |
[471045] Fix ArrayIndexOutOfBoundsException in model resolution
If we skip setValue() for particular features, then we also have to skip
setManyReference() for them. Otherwise, setManyReference() may throw an
ArrayIndexOutOfBoundsException by trying to insert values at invalid
indexes (because expected values are missing) into many-valued features.
This happenned when a non-containment EList had several elements with
some of them resolved and others not yet resolved. Then, on
endDocument(), the management of forward references invokes
setManyReference(), expecting that elements already present are indeed
present in the targeted EList, which is not the case with our parser
pool.
Bug: 471045
Change-Id: I09f014f3d3e59f7919aef091368d61de4e56a19a
Signed-off-by: Andreas Mayer <anma-e@gmx.de>
Signed-off-by: Laurent Delaigue <laurent.delaigue@obeo.fr>
4 files changed, 87 insertions, 3 deletions
diff --git a/plugins/org.eclipse.emf.compare.ide.tests/src/org/eclipse/emf/compare/ide/tests/suite/AllTests.java b/plugins/org.eclipse.emf.compare.ide.tests/src/org/eclipse/emf/compare/ide/tests/suite/AllTests.java index 6885ce3e6..de4cc5ecb 100644 --- a/plugins/org.eclipse.emf.compare.ide.tests/src/org/eclipse/emf/compare/ide/tests/suite/AllTests.java +++ b/plugins/org.eclipse.emf.compare.ide.tests/src/org/eclipse/emf/compare/ide/tests/suite/AllTests.java @@ -14,6 +14,7 @@ import junit.framework.JUnit4TestAdapter; import junit.framework.Test; import junit.textui.TestRunner; +import org.eclipse.emf.compare.ide.utils.tests.Bug471045Test; import org.eclipse.emf.compare.ide.utils.tests.ResourceUtil_BinaryIdentical2Test; import org.eclipse.emf.compare.ide.utils.tests.ResourceUtil_BinaryIdentical2_ReadLimitTest; import org.eclipse.emf.compare.ide.utils.tests.ResourceUtil_BinaryIdentical3Test; @@ -24,7 +25,8 @@ import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) @SuiteClasses({ResourceUtil_BinaryIdentical2Test.class, ResourceUtil_BinaryIdentical2_ReadLimitTest.class, - ResourceUtil_BinaryIdentical3Test.class, ResourceUtil_BinaryIdentical3_ReadLimitTest.class }) + ResourceUtil_BinaryIdentical3Test.class, ResourceUtil_BinaryIdentical3_ReadLimitTest.class, + Bug471045Test.class, }) public class AllTests { /** * Launches the test with the given arguments. diff --git a/plugins/org.eclipse.emf.compare.ide.tests/src/org/eclipse/emf/compare/ide/utils/tests/Bug471045Test.java b/plugins/org.eclipse.emf.compare.ide.tests/src/org/eclipse/emf/compare/ide/utils/tests/Bug471045Test.java new file mode 100644 index 000000000..989c1a35d --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.tests/src/org/eclipse/emf/compare/ide/utils/tests/Bug471045Test.java @@ -0,0 +1,32 @@ +package org.eclipse.emf.compare.ide.utils.tests; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.emf.compare.ide.internal.utils.NotifyingParserPool; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.xmi.XMLResource; +import org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl; +import org.junit.Test; + +@SuppressWarnings("restriction") +public class Bug471045Test { + + @Test + public void test() throws IOException { + InputStream stream = getClass().getResourceAsStream("data/bug471045.ecore"); //$NON-NLS-1$ + try { + Resource r = new XMIResourceImpl(); + NotifyingParserPool parserPool = new NotifyingParserPool(true); + Map<Object, Object> loadOptions = new HashMap<Object, Object>(); + loadOptions.put(XMLResource.OPTION_USE_PARSER_POOL, parserPool); + r.load(stream, loadOptions); + // Prior to fix, this caused a BasicIndexOutOfBoundsException + } finally { + stream.close(); + } + } + +} diff --git a/plugins/org.eclipse.emf.compare.ide.tests/src/org/eclipse/emf/compare/ide/utils/tests/data/bug471045.ecore b/plugins/org.eclipse.emf.compare.ide.tests/src/org/eclipse/emf/compare/ide/utils/tests/data/bug471045.ecore new file mode 100644 index 000000000..1f5cda0c3 --- /dev/null +++ b/plugins/org.eclipse.emf.compare.ide.tests/src/org/eclipse/emf/compare/ide/utils/tests/data/bug471045.ecore @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="p" nsURI="p" nsPrefix="p">
+ <eClassifiers xsi:type="ecore:EClass" name="0"/>
+ <eClassifiers xsi:type="ecore:EClass" name="1" eSuperTypes="//@eClassifiers.0
+ //@eClassifiers.2
+ //@eClassifiers.3
+ //@eClassifiers.4
+ //@eClassifiers.5
+ //@eClassifiers.6
+ //@eClassifiers.7"/>
+ <eClassifiers xsi:type="ecore:EClass" name="2"/>
+ <eClassifiers xsi:type="ecore:EClass" name="3"/>
+ <eClassifiers xsi:type="ecore:EClass" name="4"/>
+ <eClassifiers xsi:type="ecore:EClass" name="5"/>
+ <eClassifiers xsi:type="ecore:EClass" name="6"/>
+ <eClassifiers xsi:type="ecore:EClass" name="7"/>
+</ecore:EPackage>
diff --git a/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/NotifyingParserPool.java b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/NotifyingParserPool.java index 8b69eaa11..69b9559cb 100644 --- a/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/NotifyingParserPool.java +++ b/plugins/org.eclipse.emf.compare.ide/src/org/eclipse/emf/compare/ide/internal/utils/NotifyingParserPool.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2014, 2015 Obeo. + * Copyright (c) 2014, 2016 Obeo. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -10,9 +10,11 @@ *******************************************************************************/ package org.eclipse.emf.compare.ide.internal.utils; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; @@ -23,10 +25,12 @@ import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.util.ExtendedMetaData; +import org.eclipse.emf.ecore.xmi.XMIException; import org.eclipse.emf.ecore.xmi.XMLDefaultHandler; import org.eclipse.emf.ecore.xmi.XMLHelper; import org.eclipse.emf.ecore.xmi.XMLLoad; import org.eclipse.emf.ecore.xmi.XMLResource; +import org.eclipse.emf.ecore.xmi.impl.XMLHandler; import org.eclipse.emf.ecore.xmi.impl.XMLParserPoolImpl; import org.xml.sax.Attributes; import org.xml.sax.SAXException; @@ -280,7 +284,6 @@ public class NotifyingParserPool extends XMLParserPoolImpl { this.containmentOnly = containmentOnly; } - /** {@inheritDoc} */ @Override public void setValue(EObject eObject, EStructuralFeature eStructuralFeature, Object value, int position) { @@ -299,6 +302,35 @@ public class NotifyingParserPool extends XMLParserPoolImpl { } } + /** + * Called by {@link XMLHandler} to set the values of the given many-valued forward reference. The + * target feature may already contain values resolved from backward references and set by + * {@link #setValue(EObject, EStructuralFeature, Object, int)}. The given reference also specifies the + * insertion indexes necessary to insert the new values at the correct positions. + * <p> + * Note that the super-implementation will throw an {@link ArrayIndexOutOfBoundsException} if the + * insertion indexes do not match the contents of the target feature, that is, if it contains too few + * elements. + * + * @param reference + * The reference to set + * @param location + * The location + * @return An empty list if the reference must be ignored, and the result of the parent implementation + * otherwise. + */ + @Override + public List<XMIException> setManyReference(ManyReference reference, String location) { + EStructuralFeature eStructuralFeature = reference.getFeature(); + boolean isContainment = eStructuralFeature instanceof EReference + && ((EReference)eStructuralFeature).isContainment(); + if (!containmentOnly || isContainment) { + return super.setManyReference(reference, location); + } + + return Collections.emptyList(); + } + /** Check the {@link #potentialProxies} list for {@link EObject#eIsProxy() actual proxies}. */ public void checkProxies() { Iterator<ProxyEntry> candidateIterator = potentialProxies.iterator(); |