From 8e85cf47ed64e28db53b5596b5e2beb3cb524e7c Mon Sep 17 00:00:00 2001 From: sefftinge Date: Fri, 4 Jul 2008 13:49:07 +0000 Subject: [PATCH] parsetree reconstructor refactoring (part 1) --- .../org.eclipse.xtext/META-INF/MANIFEST.MF | 4 +- .../src/org/eclipse/xtext/GrammarUtil.ext | 3 + .../src/org/eclipse/xtext/GrammarUtil.java | 7 + .../org/eclipse/xtext/parser/IAstFactory.java | 3 + .../AbstractParseTreeConstructor.java | 40 --- .../org/eclipse/xtext/parsetree/NodeUtil.java | 12 + .../AbstractInternalParseTreeConstructor.java | 290 ------------------ .../reconstr/IInstanceDescription.java | 19 ++ .../{ => reconstr}/IParseTreeConstructor.java | 4 +- .../IParseTreeConstructorCallback.java | 14 + ...DefaultParsetreeReconstructorCallback.java | 29 ++ .../callbacks/SimpleSerializingCallback.java | 45 +++ .../impl/AbstractParseTreeConstructor.java | 59 ++++ .../reconstr/impl/InstanceDescription.java | 163 ++++++++++ .../{ => reconstr/impl}/Predicate.java | 5 +- .../eclipse/xtext/resource/XtextResource.java | 5 - .../org/eclipse/xtext/XtextGrammarTest.java | 10 - .../reconstr/ComplexReconstrTest.java | 29 +- .../reconstr/SimpleReconstrTest.java | 15 +- .../xtext/tests/AbstractGeneratorTest.java | 13 +- .../xtext2ecore/EcoreModelComparator.java | 157 ++++++---- 21 files changed, 483 insertions(+), 443 deletions(-) delete mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/AbstractParseTreeConstructor.java delete mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/internal/AbstractInternalParseTreeConstructor.java create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/IInstanceDescription.java rename plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/{ => reconstr}/IParseTreeConstructor.java (85%) create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/IParseTreeConstructorCallback.java create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/callbacks/DefaultParsetreeReconstructorCallback.java create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/callbacks/SimpleSerializingCallback.java create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/impl/AbstractParseTreeConstructor.java create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/impl/InstanceDescription.java rename plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/{ => reconstr/impl}/Predicate.java (79%) diff --git a/plugins/org.eclipse.xtext/META-INF/MANIFEST.MF b/plugins/org.eclipse.xtext/META-INF/MANIFEST.MF index 3715a3d37..0e8fbf6e9 100644 --- a/plugins/org.eclipse.xtext/META-INF/MANIFEST.MF +++ b/plugins/org.eclipse.xtext/META-INF/MANIFEST.MF @@ -17,7 +17,9 @@ Export-Package: org.eclipse.xtext, org.eclipse.xtext.parser.antlr, org.eclipse.xtext.parser.impl, org.eclipse.xtext.parsetree, - org.eclipse.xtext.parsetree.internal, + org.eclipse.xtext.parsetree.reconstr, + org.eclipse.xtext.parsetree.reconstr.callbacks, + org.eclipse.xtext.parsetree.reconstr.impl, org.eclipse.xtext.parsetree.util, org.eclipse.xtext.resource, org.eclipse.xtext.services, diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/GrammarUtil.ext b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/GrammarUtil.ext index 4b6afc9a6..e44715d14 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/GrammarUtil.ext +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/GrammarUtil.ext @@ -70,6 +70,9 @@ AbstractRule calledRule(RuleCall this) : String getReturnTypeName(AbstractRule p) : JAVA org.eclipse.xtext.GrammarUtil.getReturnTypeName(org.eclipse.xtext.AbstractRule); +Assignment containingAssignment(emf::EObject this) : + JAVA org.eclipse.xtext.GrammarUtil.containingAssignment(org.eclipse.emf.ecore.EObject); + Boolean isAssigned(emf::EObject this) : JAVA org.eclipse.xtext.GrammarUtil.isAssigned(org.eclipse.emf.ecore.EObject); diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/GrammarUtil.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/GrammarUtil.java index 0b9a3c168..12ad465d6 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/GrammarUtil.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/GrammarUtil.java @@ -34,6 +34,13 @@ import org.eclipse.xtext.util.Strings; * @author svenefftinge */ public class GrammarUtil { + + public static ParserRule getDefaultEntryRule(Grammar g) { + if (g.isAbstract()) + return null; + return GrammarUtil.allParserRules(g).get(0); + } + public static String getLanguageId(Grammar g) { return Strings.concat(".", g.getIdElements()); } diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parser/IAstFactory.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parser/IAstFactory.java index 870b76ace..90f35088e 100755 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parser/IAstFactory.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parser/IAstFactory.java @@ -9,6 +9,7 @@ package org.eclipse.xtext.parser; import org.antlr.runtime.RecognitionException; +import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.xtext.service.ILanguageService; @@ -48,4 +49,6 @@ public interface IAstFactory extends ILanguageService { public void add(EObject _this, String feature, Object value) throws RecognitionException; public void add(EObject _this, String feature, Object value, String lexerRule) throws RecognitionException; + public EClass getEClass(String fullTypeName); + } diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/AbstractParseTreeConstructor.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/AbstractParseTreeConstructor.java deleted file mode 100644 index 6b3c31862..000000000 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/AbstractParseTreeConstructor.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.eclipse.xtext.parsetree; - -import org.eclipse.xtext.Grammar; -import org.eclipse.xtext.IGrammarAccess; -import org.eclipse.xtext.conversion.IValueConverterService; -import org.eclipse.xtext.parser.GenericEcoreElementFactory; -import org.eclipse.xtext.parser.IAstFactory; -import org.eclipse.xtext.service.Inject; - -public abstract class AbstractParseTreeConstructor implements IParseTreeConstructor { - private GenericEcoreElementFactory astElementFactory; - - @Inject - protected IValueConverterService converterService; - - protected IValueConverterService getValueConverterService() { - return converterService; - } - - private Grammar grammar; - - @Inject - protected void setElementFactory(IAstFactory astElementFactory) { - this.astElementFactory = (GenericEcoreElementFactory) astElementFactory; - } - - protected GenericEcoreElementFactory getFactory() { - return astElementFactory; - } - - @Inject - public void setGrammarAccess(IGrammarAccess grammarAccess) { - this.grammar = grammarAccess.getGrammar(); - } - - protected Grammar getGrammar() { - return grammar; - } - -} diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/NodeUtil.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/NodeUtil.java index f5b329b01..ce8e6f5c8 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/NodeUtil.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/NodeUtil.java @@ -19,6 +19,7 @@ import org.eclipse.emf.ecore.util.EcoreUtil; * */ public class NodeUtil { + public static NodeAdapter getNodeAdapter(EObject obj) { return (NodeAdapter) EcoreUtil.getAdapter(obj.eAdapters(), AbstractNode.class); } @@ -81,6 +82,17 @@ public class NodeUtil { } return null; } + + public static EObject findASTParentElement(CompositeNode replaceRootNode) { + CompositeNode parent = replaceRootNode.getParent(); + if (parent == null) { + return null; + } + if (parent.getElement() != null) { + return parent.getElement(); + } + return findASTParentElement(parent); + } public static void dumpCompositeNodes(String indent, CompositeNode node) { dumpCompositeNodeInfo(indent, node); diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/internal/AbstractInternalParseTreeConstructor.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/internal/AbstractInternalParseTreeConstructor.java deleted file mode 100644 index ae972a9ad..000000000 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/internal/AbstractInternalParseTreeConstructor.java +++ /dev/null @@ -1,290 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008 itemis AG (http://www.itemis.eu) and others. - * 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 - * http://www.eclipse.org/legal/epl-v10.html - * - *******************************************************************************/ -package org.eclipse.xtext.parsetree.internal; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.eclipse.emf.common.util.EList; -import org.eclipse.emf.common.util.URI; -import org.eclipse.emf.ecore.EClass; -import org.eclipse.emf.ecore.EObject; -import org.eclipse.emf.ecore.EStructuralFeature; -import org.eclipse.emf.ecore.util.EcoreUtil; -import org.eclipse.xtext.Action; -import org.eclipse.xtext.Grammar; -import org.eclipse.xtext.GrammarUtil; -import org.eclipse.xtext.Keyword; -import org.eclipse.xtext.LexerRule; -import org.eclipse.xtext.RuleCall; -import org.eclipse.xtext.conversion.IValueConverterService; -import org.eclipse.xtext.parser.GenericEcoreElementFactory; -import org.eclipse.xtext.parsetree.AbstractNode; -import org.eclipse.xtext.parsetree.CompositeNode; -import org.eclipse.xtext.parsetree.LeafNode; -import org.eclipse.xtext.parsetree.NodeAdapter; -import org.eclipse.xtext.parsetree.NodeAdapterFactory; -import org.eclipse.xtext.parsetree.ParsetreeFactory; - -/** - * @author Sven Efftinge - Initial contribution and API - * - */ -public abstract class AbstractInternalParseTreeConstructor { - - - private GenericEcoreElementFactory factory; - private Grammar grammar; - private IValueConverterService converterService; - - public AbstractInternalParseTreeConstructor(GenericEcoreElementFactory factory, Grammar grammar, IValueConverterService converterService) { - this.factory =factory; - this.grammar = grammar; - this.converterService = converterService; - } - - protected Grammar getGrammar() { - return grammar; - } - - protected GenericEcoreElementFactory getFactory() { - return factory; - } - - public void update(EObject object) { - NodeAdapter adapter = getAdapter(object); - CompositeNode rootNode = null; - String ruleToCall = GrammarUtil.allParserRules(getGrammar()).get(0).getName(); - if (adapter == null) { - rootNode = ParsetreeFactory.eINSTANCE.createCompositeNode(); - object = EcoreUtil.getRootContainer(object); - // TODO set synthetic rulecall - } else { - rootNode = adapter.getParserNode(); - rootNode.getChildren().clear(); - if (rootNode.getGrammarElement() instanceof RuleCall) { - ruleToCall = ((RuleCall) rootNode.getGrammarElement()).getName(); - } - object = rootNode.getElement(); - } - internalDoUpdate(object, ruleToCall); - } - - protected abstract void internalDoUpdate(EObject obj, String ruleToCall); - - private NodeAdapter getAdapter(EObject object) { - NodeAdapter adapter = (NodeAdapter) EcoreUtil.getAdapter(object.eAdapters(), AbstractNode.class); - if ((adapter == null || !(adapter.getParserNode().getGrammarElement() instanceof RuleCall)) - && object.eContainer() != null) { - return getAdapter(object.eContainer()); - } - return adapter; - } - - protected final InstanceDescription getDescr(InstanceDescription obj) { - return obj; - } - - protected final InstanceDescription getDescr(EObject obj) { - return new InstanceDescription(obj, false); - } - - protected final InstanceDescription getDescr(EObject obj, boolean lookahead) { - return new InstanceDescription(obj, lookahead); - } - - public final class InstanceDescription { - - public boolean isInstanceOf(String string) { - EClass class1 = getFactory().getEClass(string); - return class1 != null && class1.isSuperTypeOf(getDelegate().eClass()); - } - - public boolean isOfType(String string) { - EClass class1 = getFactory().getEClass(string); - return class1 != null && class1.equals(getDelegate().eClass()); - } - - public EObject getDelegate() { - return described; - } - - private boolean isLookahead = false; - - private EObject described; - private Map featureConsumedCounter = new HashMap(); - - public InstanceDescription(EObject described, boolean lookahead) { - super(); - if (described == null) - throw new NullPointerException("described"); - this.described = described; - this.isLookahead = lookahead; - EList features = described.eClass().getEAllStructuralFeatures(); - for (EStructuralFeature f : features) { - Integer integer = 0; - if (described.eIsSet(f)) { - if (f.isMany()) { - integer = ((List) described.eGet(f)).size(); - } else { - integer = 1; - } - } - featureConsumedCounter.put(f.getName(), integer); - } - } - - private InstanceDescription(EObject described, Map featureConsumedCounter) { - super(); - this.isLookahead = true; - this.described = described; - this.featureConsumedCounter = featureConsumedCounter; - } - - public boolean isLookahead() { - return isLookahead; - } - - @Override - public String toString() { - return hashCode() + "/" + described.hashCode(); - } - - public Object consume(String feature) { - if (!isConsumable(feature)) - throw new IllegalStateException(feature + " is not consumable"); - Integer counter = lazyGet(feature); - EStructuralFeature f = getFeature(feature); - Object get = described.eGet(f); - if (f.isMany()) { - List list = (List) get; - get = list.get(counter - 1); - } - featureConsumedCounter.put(feature, counter - 1); - return get; - } - - public boolean checkConsume(String feature) { - if (!isConsumable(feature)) - return false; - Integer counter = lazyGet(feature); - EStructuralFeature f = getFeature(feature); - Object get = described.eGet(f); - if (f.isMany()) { - List list = (List) get; - get = list.get(counter - 1); - } - featureConsumedCounter.put(feature, counter - 1); - return true; - } - - private Integer lazyGet(String feature) { - Integer integer = featureConsumedCounter.get(feature); - if (integer == null) { - return 0; - } - return integer; - } - - private EStructuralFeature getFeature(String feature) { - return described.eClass().getEStructuralFeature(feature); - } - - public boolean isConsumable(String feature) { - return lazyGet(feature) > 0; - } - - public boolean isConsumed() { - for (Integer i : featureConsumedCounter.values()) { - if (i > 0) - return false; - } - return true; - } - - public InstanceDescription newLookaheadDescription() { - return new InstanceDescription(described, new HashMap(featureConsumedCounter)); - } - - } - - private CompositeNode current = ParsetreeFactory.eINSTANCE.createCompositeNode(); - private LexerRule wsRule; - - protected void ruleCallStart(InstanceDescription val, boolean isAssigned, RuleCall ruleCall) { - CompositeNode node = ParsetreeFactory.eINSTANCE.createCompositeNode(); - node.setGrammarElement(ruleCall); - prependToCurrentsChildren(node); - current = node; - } - - protected void ruleCallEnd(InstanceDescription val, boolean b, RuleCall ruleCall) { - current = current.getParent(); - } - - private void prependToCurrentsChildren(AbstractNode node) { - current.getChildren().add(0, node); - } - - protected void lexerRuleCall(RuleCall ruleCall) { - throw new UnsupportedOperationException("coudn't generate text for lexer rule " + ruleCall.getName()); - } - - protected void lexerRuleCall(Object value, RuleCall ruleCall) { - checkWhitespace(); - LeafNode ln = ParsetreeFactory.eINSTANCE.createLeafNode(); - ln.setGrammarElement(ruleCall); - ln.setText(converterService.toString(value, ruleCall.getName())); - prependToCurrentsChildren(ln); - } - - private void checkWhitespace() { - EList leafNodes = ((CompositeNode) EcoreUtil.getRootContainer(current)).getLeafNodes(); - if (!leafNodes.isEmpty()) { - LeafNode next = leafNodes.get(0); - if (!next.isHidden()) { - LeafNode ws = ParsetreeFactory.eINSTANCE.createLeafNode(); - ws.setText(" "); - ws.setGrammarElement(getWhitespaceRule()); - prependToCurrentsChildren(ws); - } - } - } - - private LexerRule getWhitespaceRule() { - if (wsRule == null) { - // TODO - } - return wsRule; - } - - protected void action(InstanceDescription parent, InstanceDescription child, Action action) { - objectCreation(parent); - } - - protected void keyword(Keyword kw) { - checkWhitespace(); - LeafNode ln = ParsetreeFactory.eINSTANCE.createLeafNode(); - ln.setGrammarElement(kw); - ln.setText(converterService.toString(kw.getValue(), kw.getValue())); - prependToCurrentsChildren(ln); - } - - protected void objectCreation(InstanceDescription obj) { - current.setElement(obj.getDelegate()); - NodeAdapter newOne = (NodeAdapter) NodeAdapterFactory.INSTANCE.adapt(obj.getDelegate(), AbstractNode.class); - newOne.setParserNode(current); - } - - protected EObject getGrammarElement(String string) { - return getGrammar().eResource().getResourceSet().getEObject(URI.createURI(string), true); - } - -} diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/IInstanceDescription.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/IInstanceDescription.java new file mode 100644 index 000000000..339c4eb6d --- /dev/null +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/IInstanceDescription.java @@ -0,0 +1,19 @@ +package org.eclipse.xtext.parsetree.reconstr; + +import org.eclipse.emf.ecore.EObject; + +public interface IInstanceDescription { + + public abstract boolean isInstanceOf(String string); + + public abstract boolean isOfType(String string); + + public abstract EObject getDelegate(); + + public abstract Object get(String feature); + + public abstract boolean isConsumable(String feature); + + public abstract boolean isConsumed(); + +} \ No newline at end of file diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/IParseTreeConstructor.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/IParseTreeConstructor.java similarity index 85% rename from plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/IParseTreeConstructor.java rename to plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/IParseTreeConstructor.java index ea0888dab..c3dded1e8 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/IParseTreeConstructor.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/IParseTreeConstructor.java @@ -6,7 +6,7 @@ * http://www.eclipse.org/legal/epl-v10.html * *******************************************************************************/ -package org.eclipse.xtext.parsetree; +package org.eclipse.xtext.parsetree.reconstr; import org.eclipse.emf.ecore.EObject; import org.eclipse.xtext.service.ILanguageService; @@ -16,5 +16,5 @@ import org.eclipse.xtext.service.ILanguageService; * */ public interface IParseTreeConstructor extends ILanguageService { - public void update(EObject object); + public void update(EObject object, IParseTreeConstructorCallback callback); } diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/IParseTreeConstructorCallback.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/IParseTreeConstructorCallback.java new file mode 100644 index 000000000..b56b06a75 --- /dev/null +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/IParseTreeConstructorCallback.java @@ -0,0 +1,14 @@ +package org.eclipse.xtext.parsetree.reconstr; + +import org.eclipse.xtext.Action; +import org.eclipse.xtext.Keyword; +import org.eclipse.xtext.RuleCall; + +public interface IParseTreeConstructorCallback { + void parserRuleCallStart(IInstanceDescription current, RuleCall call); + void parserRuleCallEnd(); + void objectCreation(IInstanceDescription current); + void lexerRuleCall(IInstanceDescription current, RuleCall call); + void keywordCall(IInstanceDescription current, Keyword call); + void actionCall(IInstanceDescription oldCurrent,IInstanceDescription newCurrent, Action action); +} diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/callbacks/DefaultParsetreeReconstructorCallback.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/callbacks/DefaultParsetreeReconstructorCallback.java new file mode 100644 index 000000000..8f1293584 --- /dev/null +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/callbacks/DefaultParsetreeReconstructorCallback.java @@ -0,0 +1,29 @@ +package org.eclipse.xtext.parsetree.reconstr.callbacks; + +import org.eclipse.xtext.Action; +import org.eclipse.xtext.Keyword; +import org.eclipse.xtext.RuleCall; +import org.eclipse.xtext.parsetree.reconstr.IInstanceDescription; +import org.eclipse.xtext.parsetree.reconstr.IParseTreeConstructorCallback; + +public class DefaultParsetreeReconstructorCallback implements IParseTreeConstructorCallback { + + public void actionCall(IInstanceDescription oldCurrent, IInstanceDescription newCurrent, Action action) { + } + + public void keywordCall(IInstanceDescription current, Keyword call) { + } + + public void lexerRuleCall(IInstanceDescription current, RuleCall call) { + } + + public void objectCreation(IInstanceDescription current) { + } + + public void parserRuleCallEnd() { + } + + public void parserRuleCallStart(IInstanceDescription current, RuleCall call) { + } + +} diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/callbacks/SimpleSerializingCallback.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/callbacks/SimpleSerializingCallback.java new file mode 100644 index 000000000..eef10da33 --- /dev/null +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/callbacks/SimpleSerializingCallback.java @@ -0,0 +1,45 @@ +package org.eclipse.xtext.parsetree.reconstr.callbacks; + +import org.eclipse.xtext.Assignment; +import org.eclipse.xtext.GrammarUtil; +import org.eclipse.xtext.Keyword; +import org.eclipse.xtext.RuleCall; +import org.eclipse.xtext.conversion.IValueConverterService; +import org.eclipse.xtext.parsetree.reconstr.IInstanceDescription; + +public class SimpleSerializingCallback extends DefaultParsetreeReconstructorCallback { + private StringBuffer buff = new StringBuffer(); + private IValueConverterService converterService; + + public SimpleSerializingCallback(IValueConverterService converterService) { + this.converterService = converterService; + } + + @Override + public void keywordCall(IInstanceDescription current, Keyword call) { + if (buff.length()>0) + prepend(" "); + prepend(call.getValue()); + } + + private void prepend(String s) { + buff.insert(0, s); + } + + @Override + public void lexerRuleCall(IInstanceDescription current, RuleCall call) { + Assignment assignment = GrammarUtil.containingAssignment(call); + Object value = null; + if (assignment!=null) { + value = current.get(assignment.getFeature()); + } + if (buff.length()>0) + prepend(" "); + prepend(converterService.toString(value, call.getName())); + } + + @Override + public String toString() { + return buff.toString(); + } +} diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/impl/AbstractParseTreeConstructor.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/impl/AbstractParseTreeConstructor.java new file mode 100644 index 000000000..8037f7d7b --- /dev/null +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/impl/AbstractParseTreeConstructor.java @@ -0,0 +1,59 @@ +package org.eclipse.xtext.parsetree.reconstr.impl; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.xtext.Grammar; +import org.eclipse.xtext.GrammarUtil; +import org.eclipse.xtext.IGrammarAccess; +import org.eclipse.xtext.ParserRule; +import org.eclipse.xtext.conversion.IValueConverterService; +import org.eclipse.xtext.parser.IAstFactory; +import org.eclipse.xtext.parsetree.reconstr.IInstanceDescription; +import org.eclipse.xtext.parsetree.reconstr.IParseTreeConstructor; +import org.eclipse.xtext.parsetree.reconstr.IParseTreeConstructorCallback; +import org.eclipse.xtext.service.Inject; + +public abstract class AbstractParseTreeConstructor implements IParseTreeConstructor { + @Inject + private IAstFactory factory; + + @Inject + protected IValueConverterService converterService; + + @Inject + private IGrammarAccess grammar; + + public IAstFactory getFactory() { + return factory; + } + + protected Grammar getGrammar() { + return grammar.getGrammar(); + } + + protected IValueConverterService getValueConverterService() { + return converterService; + } + + + public void update(EObject object, IParseTreeConstructorCallback callback) { + EObject root = EcoreUtil.getRootContainer(object); + ParserRule parserRule = GrammarUtil.getDefaultEntryRule(getGrammar()); + String ruleToCall = parserRule.getName(); + internalDoUpdate(root, ruleToCall,callback); + } + protected abstract void internalDoUpdate(EObject obj, String ruleToCall, IParseTreeConstructorCallback callback); + + protected final InstanceDescription getDescr(EObject obj) { + return new InstanceDescription(this, obj); + } + + protected final IInstanceDescription getDescr(IInstanceDescription obj) { + return obj; + } + + protected EObject getGrammarElement(String string) { + return grammar.getGrammar().eResource().getResourceSet().getEObject(URI.createURI(string), true); + } +} diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/impl/InstanceDescription.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/impl/InstanceDescription.java new file mode 100644 index 000000000..417280c20 --- /dev/null +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/impl/InstanceDescription.java @@ -0,0 +1,163 @@ +/******************************************************************************* + * Copyright (c) 2008 itemis AG (http://www.itemis.eu) and others. + * 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 + * http://www.eclipse.org/legal/epl-v10.html + * + *******************************************************************************/ +package org.eclipse.xtext.parsetree.reconstr.impl; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.xtext.parsetree.reconstr.IInstanceDescription; + +public class InstanceDescription implements IInstanceDescription { + + /** + * + */ + private AbstractParseTreeConstructor parseTreeConstr; + + /* (non-Javadoc) + * @see org.eclipse.xtext.parsetree.impl.IInstanceDescription#isInstanceOf(java.lang.String) + */ + public boolean isInstanceOf(String string) { + EClass class1 = this.parseTreeConstr.getFactory().getEClass(string); + return class1 != null && class1.isSuperTypeOf(getDelegate().eClass()); + } + + /* (non-Javadoc) + * @see org.eclipse.xtext.parsetree.impl.IInstanceDescription#isOfType(java.lang.String) + */ + public boolean isOfType(String string) { + EClass class1 = this.parseTreeConstr.getFactory().getEClass(string); + return class1 != null && class1.equals(getDelegate().eClass()); + } + + /* (non-Javadoc) + * @see org.eclipse.xtext.parsetree.impl.IInstanceDescription#getDelegate() + */ + public EObject getDelegate() { + return described; + } + + private EObject described; + private Map featureConsumedCounter = new HashMap(); + + public InstanceDescription(AbstractParseTreeConstructor abstractInternalParseTreeConstructor, EObject described) { + super(); + this.parseTreeConstr = abstractInternalParseTreeConstructor; + if (described == null) + throw new NullPointerException("described"); + this.described = described; + EList features = described.eClass().getEAllStructuralFeatures(); + for (EStructuralFeature f : features) { + Integer integer = 0; + if (described.eIsSet(f)) { + if (f.isMany()) { + integer = ((List) described.eGet(f)).size(); + } else { + integer = 1; + } + } + featureConsumedCounter.put(f.getName(), integer); + } + } + + private InstanceDescription(AbstractParseTreeConstructor abstractInternalParseTreeConstructor, EObject described, Map featureConsumedCounter) { + super(); + this.parseTreeConstr = abstractInternalParseTreeConstructor; + this.described = described; + this.featureConsumedCounter = featureConsumedCounter; + } + + + @Override + public String toString() { + return hashCode() + "/" + described.hashCode(); + } + + public Object consume(String feature) { + if (!isConsumable(feature)) + throw new IllegalStateException(feature + " is not consumable"); + Integer counter = lazyGet(feature); + EStructuralFeature f = getFeature(feature); + Object get = described.eGet(f); + if (f.isMany()) { + List list = (List) get; + get = list.get(counter - 1); + } + featureConsumedCounter.put(feature, counter - 1); + return get; + } + + /* (non-Javadoc) + * @see org.eclipse.xtext.parsetree.impl.IInstanceDescription#get(java.lang.String) + */ + public Object get(String feature) { + Integer counter = lazyGet(feature); + EStructuralFeature f = getFeature(feature); + Object get = described.eGet(f); + if (f.isMany()) { + List list = (List) get; + get = list.get(counter); + } + return get; + } + + public boolean checkConsume(String feature) { + if (!isConsumable(feature)) + return false; + Integer counter = lazyGet(feature); + EStructuralFeature f = getFeature(feature); + Object get = described.eGet(f); + if (f.isMany()) { + List list = (List) get; + get = list.get(counter - 1); + } + featureConsumedCounter.put(feature, counter - 1); + return true; + } + + private Integer lazyGet(String feature) { + Integer integer = featureConsumedCounter.get(feature); + if (integer == null) { + return 0; + } + return integer; + } + + private EStructuralFeature getFeature(String feature) { + return described.eClass().getEStructuralFeature(feature); + } + + /* (non-Javadoc) + * @see org.eclipse.xtext.parsetree.impl.IInstanceDescription#isConsumable(java.lang.String) + */ + public boolean isConsumable(String feature) { + return lazyGet(feature) > 0; + } + + /* (non-Javadoc) + * @see org.eclipse.xtext.parsetree.impl.IInstanceDescription#isConsumed() + */ + public boolean isConsumed() { + for (Integer i : featureConsumedCounter.values()) { + if (i > 0) + return false; + } + return true; + } + + public InstanceDescription clone() { + return new InstanceDescription(this.parseTreeConstr, described, new HashMap(featureConsumedCounter)); + } + +} \ No newline at end of file diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/Predicate.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/impl/Predicate.java similarity index 79% rename from plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/Predicate.java rename to plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/impl/Predicate.java index af2af3498..6d5be5b11 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/Predicate.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/impl/Predicate.java @@ -6,9 +6,8 @@ * http://www.eclipse.org/legal/epl-v10.html * *******************************************************************************/ -package org.eclipse.xtext.parsetree; +package org.eclipse.xtext.parsetree.reconstr.impl; -import org.eclipse.xtext.parsetree.internal.AbstractInternalParseTreeConstructor.InstanceDescription; /** @@ -20,7 +19,7 @@ public abstract class Predicate { protected InstanceDescription obj; public Predicate(InstanceDescription obj) { - this.obj = obj.newLookaheadDescription(); + this.obj = obj.clone(); } public abstract boolean check(); diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/XtextResource.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/XtextResource.java index a1089fa42..0b6aff22f 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/XtextResource.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/XtextResource.java @@ -23,7 +23,6 @@ import org.eclipse.xtext.parser.IParseResult; import org.eclipse.xtext.parser.IParser; import org.eclipse.xtext.parsetree.AbstractNode; import org.eclipse.xtext.parsetree.CompositeNode; -import org.eclipse.xtext.parsetree.IParseTreeConstructor; import org.eclipse.xtext.parsetree.NodeAdapter; import org.eclipse.xtext.parsetree.NodeContentAdapter; import org.eclipse.xtext.service.Inject; @@ -40,9 +39,6 @@ public class XtextResource extends ResourceImpl { @Inject private IAstFactory elementFactory; - @Inject - private IParseTreeConstructor parsetreeConstructor; - private IParseResult parseResult; public XtextResource(URI uri) { @@ -95,7 +91,6 @@ public class XtextResource extends ResourceImpl { } if (!contents.isEmpty()) { EObject rootElement = contents.get(0); - parsetreeConstructor.update(rootElement); NodeAdapter rootNodeAdapter = getNodeAdapter(rootElement); if (rootNodeAdapter != null) { CompositeNode rootNode = rootNodeAdapter.getParserNode(); diff --git a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/XtextGrammarTest.java b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/XtextGrammarTest.java index 5911bb866..bb6e92637 100644 --- a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/XtextGrammarTest.java +++ b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/XtextGrammarTest.java @@ -10,8 +10,6 @@ package org.eclipse.xtext; import org.eclipse.emf.ecore.EObject; -import org.eclipse.xtext.parsetree.NodeUtil; -import org.eclipse.xtext.parsetree.XtextGrammarTestParseTreeConstructor; import org.eclipse.xtext.tests.AbstractGeneratorTest; /** @@ -32,12 +30,4 @@ public class XtextGrammarTest extends AbstractGeneratorTest { assertWithXtend("'name'","parserRules.first().alternatives.feature",grammar); } - public void testSerialization() throws Exception { - String model = "generate foo 'bar' Foo : ( 'stuff' '{' '}' STRING ) ? ;"; - EObject grammar = (EObject) getModel(model); - XtextGrammarTestParseTreeConstructor ptc = (XtextGrammarTestParseTreeConstructor) getParseTreeConstructor(); - ptc.update(grammar); - assertEquals(model, NodeUtil.getNodeAdapter(grammar).getParserNode().serialize()); - } - } diff --git a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/parsetree/reconstr/ComplexReconstrTest.java b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/parsetree/reconstr/ComplexReconstrTest.java index 9baf83328..ac2b34512 100644 --- a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/parsetree/reconstr/ComplexReconstrTest.java +++ b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/parsetree/reconstr/ComplexReconstrTest.java @@ -9,9 +9,9 @@ package org.eclipse.xtext.parsetree.reconstr; import org.eclipse.emf.ecore.EObject; -import org.eclipse.xtext.parsetree.IParseTreeConstructor; -import org.eclipse.xtext.parsetree.NodeUtil; +import org.eclipse.xtext.parsetree.reconstr.callbacks.SimpleSerializingCallback; import org.eclipse.xtext.tests.AbstractGeneratorTest; +import org.eclipse.xtext.xtext2ecore.EcoreModelComparator; public class ComplexReconstrTest extends AbstractGeneratorTest { @@ -34,16 +34,21 @@ public class ComplexReconstrTest extends AbstractGeneratorTest { private String parseAndSerialize(String model) throws Exception { EObject result = (EObject) getModel(model); IParseTreeConstructor con = getParseTreeConstructor(); - con.update(result); - String resultString = NodeUtil.getRootNode(result).serialize(); - return resultString; + SimpleSerializingCallback callback = new SimpleSerializingCallback(getValueConverterService()); + con.update(result,callback); + return callback.toString(); + } + + public void testNormalizableCompositeNodesIncluded() throws Exception { + reconstructAndCompare("a"); + reconstructAndCompare("a + b"); + } + + private void reconstructAndCompare(String mymodel) throws Exception, InterruptedException { + EObject model = getModel(mymodel); + EObject model2 = getModel(parseAndSerialize(mymodel)); + EcoreModelComparator ecoreModelComparator = new EcoreModelComparator(); + assertFalse(ecoreModelComparator.modelsDiffer(model, model2)); } -// public void testNormalizableCompositeNodesIncluded() throws Exception { -// EObject model = getModel("a"); -// IParseTreeConstructor con = getParseTreeConstructor(); -// con.update(model); -// CompositeNode node = NodeUtil.getRootNode(model); -// assertEquals("Op",((RuleCall)node.getGrammarElement()).getName()); -// } } diff --git a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/parsetree/reconstr/SimpleReconstrTest.java b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/parsetree/reconstr/SimpleReconstrTest.java index d918237ba..09832e14b 100644 --- a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/parsetree/reconstr/SimpleReconstrTest.java +++ b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/parsetree/reconstr/SimpleReconstrTest.java @@ -9,8 +9,7 @@ package org.eclipse.xtext.parsetree.reconstr; import org.eclipse.emf.ecore.EObject; -import org.eclipse.xtext.parsetree.IParseTreeConstructor; -import org.eclipse.xtext.parsetree.NodeUtil; +import org.eclipse.xtext.parsetree.reconstr.callbacks.SimpleSerializingCallback; import org.eclipse.xtext.testlanguages.SimpleExpressionsStandaloneSetup; import org.eclipse.xtext.tests.AbstractGeneratorTest; @@ -31,19 +30,15 @@ public class SimpleReconstrTest extends AbstractGeneratorTest { private String parseAndSerialize(String model) throws Exception { EObject result = (EObject) getModel(model); IParseTreeConstructor con = getParseTreeConstructor(); - con.update(result); - String resultString = NodeUtil.getRootNode(result).serialize(); - return resultString; + SimpleSerializingCallback callback = new SimpleSerializingCallback(getValueConverterService()); + con.update(result, callback); + return callback.toString(); } public void testSimpleExpressions() throws Exception { with(SimpleExpressionsStandaloneSetup.class); String model = "a + b - c * d / e"; - EObject result = (EObject) getModel(model); - IParseTreeConstructor con = getParseTreeConstructor(); - con.update(result); - String resultString = NodeUtil.getRootNode(result).serialize(); - assertEquals(model,resultString); + assertEquals(model,parseAndSerialize(model)); } @Override diff --git a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/tests/AbstractGeneratorTest.java b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/tests/AbstractGeneratorTest.java index 4fb3f474d..2b9fabf2f 100755 --- a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/tests/AbstractGeneratorTest.java +++ b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/tests/AbstractGeneratorTest.java @@ -22,10 +22,11 @@ import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.m2t.type.emf.EmfRegistryMetaModel; import org.eclipse.xtext.GenerateAllTestGrammars; import org.eclipse.xtext.XtextStandaloneSetup; +import org.eclipse.xtext.conversion.IValueConverterService; import org.eclipse.xtext.parser.IAstFactory; import org.eclipse.xtext.parser.IParser; import org.eclipse.xtext.parsetree.CompositeNode; -import org.eclipse.xtext.parsetree.IParseTreeConstructor; +import org.eclipse.xtext.parsetree.reconstr.IParseTreeConstructor; import org.eclipse.xtext.resource.IResourceFactory; import org.eclipse.xtext.resource.XtextResource; import org.eclipse.xtext.resource.XtextResourceSet; @@ -89,17 +90,21 @@ public abstract class AbstractGeneratorTest extends TestCase { return ServiceRegistry.getService(currentLanguageDescriptor, IParser.class); } - protected IAstFactory getASTFactory() throws Exception { + protected IAstFactory getASTFactory() { return ServiceRegistry.getService(currentLanguageDescriptor, IAstFactory.class); } - protected IParseTreeConstructor getParseTreeConstructor() throws Exception { + protected IParseTreeConstructor getParseTreeConstructor() { return ServiceRegistry.getService(currentLanguageDescriptor, IParseTreeConstructor.class); } - protected IResourceFactory getResourceFactory() throws Exception { + protected IResourceFactory getResourceFactory() { return ServiceRegistry.getService(currentLanguageDescriptor, IResourceFactory.class); } + + protected IValueConverterService getValueConverterService() { + return ServiceRegistry.getService(currentLanguageDescriptor, IValueConverterService.class); + } // parse methods diff --git a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/xtext2ecore/EcoreModelComparator.java b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/xtext2ecore/EcoreModelComparator.java index 2a44bd895..ebee2558f 100644 --- a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/xtext2ecore/EcoreModelComparator.java +++ b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/xtext2ecore/EcoreModelComparator.java @@ -30,6 +30,8 @@ import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.impl.ResourceImpl; +import org.eclipse.emf.ecore.util.EcoreUtil; /** * @author Jan Köhnlein @@ -37,78 +39,101 @@ import org.eclipse.emf.ecore.resource.Resource; */ public class EcoreModelComparator { - private Map options; - private IMatchEngine matchEngine; - private List ignoredFeatures = new ArrayList(); + private Map options; + private IMatchEngine matchEngine; + private List ignoredFeatures = new ArrayList(); - public EcoreModelComparator() { - options = new HashMap(); - options.put(MatchOptions.OPTION_DISTINCT_METAMODELS, Boolean.TRUE); - matchEngine = new GenericMatchEngine(); - } + public EcoreModelComparator() { + options = new HashMap(); + options.put(MatchOptions.OPTION_DISTINCT_METAMODELS, Boolean.TRUE); + matchEngine = new GenericMatchEngine(); + } - public boolean modelsDiffer(Resource left, Resource right) throws InterruptedException { - MatchModel matchModel = matchEngine.resourceMatch(left, right, options); - return modelsDiffer(matchModel); - } + public boolean modelsDiffer(Resource left, Resource right) throws InterruptedException { + MatchModel matchModel = matchEngine.resourceMatch(left, right, options); + return modelsDiffer(matchModel); + } - public boolean modelsDiffer(EObject left, EObject right) throws InterruptedException { - MatchModel matchModel = matchEngine.modelMatch(left, right, options); - return modelsDiffer(matchModel); - } + public boolean modelsDiffer(EObject left, EObject right) throws InterruptedException { + try { + addSyntheticResource(left); + addSyntheticResource(right); + MatchModel matchModel = matchEngine.modelMatch(left, right, options); + return modelsDiffer(matchModel); + } finally { + removeSyntheticResource(left); + removeSyntheticResource(right); + } + } - private boolean modelsDiffer(MatchModel matchModel) { - boolean modelsDiffer = false; - DiffModel diffModel = DiffService.doDiff(matchModel); - if (diffModel != null) { - for (DiffElement diffElement : diffModel.getOwnedElements()) { - modelsDiffer |= checkDiff(diffElement); - } - } - return modelsDiffer; - } + private class SyntheticResource extends ResourceImpl { + + } - public void addIgnoredFeature(EStructuralFeature feature) { - ignoredFeatures.add(feature); - } + private void removeSyntheticResource(EObject o) { + if (o.eResource() instanceof SyntheticResource) { + o.eResource().getContents().clear(); + } + } - private boolean checkDiff(DiffElement diffElement) { - boolean hasDiff = false; - if (!ignoreDiff(diffElement)) { - printDiff(diffElement); - hasDiff = true; - } - for (DiffElement childDiffElement : diffElement.getSubDiffElements()) { - hasDiff |= checkDiff(childDiffElement); - } - return hasDiff; - } + private void addSyntheticResource(EObject o) { + if (o.eResource() == null) { + new SyntheticResource().getContents().add(EcoreUtil.getRootContainer(o)); + } + } - private boolean ignoreDiff(DiffElement diffElement) { - if (diffElement instanceof AttributeChange) { - return ignoredFeatures.contains(((AttributeChange) diffElement).getAttribute()); - } else if (diffElement instanceof ReferenceChange) { - return ignoredFeatures.contains(((ReferenceChange) diffElement).getReference()); - } - return diffElement instanceof DiffGroup; - } + private boolean modelsDiffer(MatchModel matchModel) { + boolean modelsDiffer = false; + DiffModel diffModel = DiffService.doDiff(matchModel); + if (diffModel != null) { + for (DiffElement diffElement : diffModel.getOwnedElements()) { + modelsDiffer |= checkDiff(diffElement); + } + } + return modelsDiffer; + } - private void printDiff(DiffElement diffElement) { - if (diffElement instanceof AttributeChange) { - AttributeChange change = (AttributeChange) diffElement; - EAttribute attribute = change.getAttribute(); - System.err.println("Detected attribute difference: " + attribute.getName()); - System.err.println("\t" + change.getLeftElement()); - System.err.println("\t" + change.getRightElement()); - } else if (diffElement instanceof ReferenceChange) { - ReferenceChange change = (ReferenceChange) diffElement; - EReference reference = change.getReference(); - System.err.println("Detected reference difference: " + reference.getName()); - System.err.println("\t" + change.getLeftElement()); - System.err.println("\t" + change.getRightElement()); - } else { - // TODO: add more sysouts here... - System.err.println(diffElement.toString()); - } - } + public void addIgnoredFeature(EStructuralFeature feature) { + ignoredFeatures.add(feature); + } + + private boolean checkDiff(DiffElement diffElement) { + boolean hasDiff = false; + if (!ignoreDiff(diffElement)) { + printDiff(diffElement); + hasDiff = true; + } + for (DiffElement childDiffElement : diffElement.getSubDiffElements()) { + hasDiff |= checkDiff(childDiffElement); + } + return hasDiff; + } + + private boolean ignoreDiff(DiffElement diffElement) { + if (diffElement instanceof AttributeChange) { + return ignoredFeatures.contains(((AttributeChange) diffElement).getAttribute()); + } else if (diffElement instanceof ReferenceChange) { + return ignoredFeatures.contains(((ReferenceChange) diffElement).getReference()); + } + return diffElement instanceof DiffGroup; + } + + private void printDiff(DiffElement diffElement) { + if (diffElement instanceof AttributeChange) { + AttributeChange change = (AttributeChange) diffElement; + EAttribute attribute = change.getAttribute(); + System.err.println("Detected attribute difference: " + attribute.getName()); + System.err.println("\t" + change.getLeftElement()); + System.err.println("\t" + change.getRightElement()); + } else if (diffElement instanceof ReferenceChange) { + ReferenceChange change = (ReferenceChange) diffElement; + EReference reference = change.getReference(); + System.err.println("Detected reference difference: " + reference.getName()); + System.err.println("\t" + change.getLeftElement() +" "+reference.getName()+" = "+change.getLeftElement().eGet(reference)); + System.err.println("\t" + change.getRightElement()+" "+reference.getName()+" = "+change.getRightElement().eGet(reference)); + } else { + // TODO: add more sysouts here... + System.err.println(diffElement.toString()); + } + } }