### Eclipse Workspace Patch 1.0 #P org.eclipse.gmf.codegen.ui Index: src/org/eclipse/gmf/internal/codegen/GMFGenConfig.java =================================================================== RCS file: /cvsroot/technology/org.eclipse.gmf/plugins/org.eclipse.gmf.codegen.ui/src/org/eclipse/gmf/internal/codegen/GMFGenConfig.java,v retrieving revision 1.11 diff -u -r1.11 GMFGenConfig.java --- src/org/eclipse/gmf/internal/codegen/GMFGenConfig.java 6 Jun 2006 17:13:01 -0000 1.11 +++ src/org/eclipse/gmf/internal/codegen/GMFGenConfig.java 12 Jun 2006 19:59:22 -0000 @@ -17,6 +17,7 @@ import org.eclipse.gmf.codegen.gmfgen.GMFGenPackage; import org.eclipse.gmf.codegen.gmfgen.GenChildContainer; import org.eclipse.gmf.codegen.gmfgen.GenNode; +import org.eclipse.gmf.internal.common.reconcile.Copier; import org.eclipse.gmf.internal.common.reconcile.DefaultDecisionMaker; import org.eclipse.gmf.internal.common.reconcile.Matcher; import org.eclipse.gmf.internal.common.reconcile.ReconcilerConfigBase; @@ -57,6 +58,14 @@ preserveIfSet(GMFGEN.getGenDiagram(), GMFGEN.getShortcuts_ShortcutsProvidedFor()); preserveIfSet(GMFGEN.getGenDiagram(), GMFGEN.getEditorCandies_CreationWizardIconPath()); preserveIfSet(GMFGEN.getGenDiagram(), GMFGEN.getGenDiagram_Synchronized()); + preserveIfSet(GMFGEN.getGenDiagram(), GMFGEN.getBatchValidation_ValidationEnabled()); + preserveIfSet(GMFGEN.getGenDiagram(), GMFGEN.getBatchValidation_ValidationDecorators()); + preserveIfSet(GMFGEN.getGenDiagram(), GMFGEN.getBatchValidation_ValidationDecoratorProviderClassName()); + preserveIfSet(GMFGEN.getGenDiagram(), GMFGEN.getBatchValidation_ValidationDecoratorProviderPriority()); + preserveIfSet(GMFGEN.getGenDiagram(), GMFGEN.getBatchValidation_ValidationProviderClassName()); + preserveIfSet(GMFGEN.getGenDiagram(), GMFGEN.getBatchValidation_ValidationProviderPriority()); + preserveIfSet(GMFGEN.getGenDiagram(), GMFGEN.getBatchValidation_MetricProviderPriority()); + preserveIfSet(GMFGEN.getGenDiagram(), GMFGEN.getBatchValidation_MetricProviderClassName()); setMatcher(GMFGEN.getGenTopLevelNode(), getGenNodeMatcher()); preserveIfNotByPattern(GMFGEN.getGenTopLevelNode(), GMFGEN.getGenChildContainer_CanonicalEditPolicyClassName(), ".*" + GenChildContainer.CANONICAL_EDIT_POLICY_SUFFIX); @@ -71,16 +80,16 @@ preserveIfSet(GMFGEN.getGenCompartment(), GMFGEN.getGenCompartment_CanCollapse()); preserveIfSet(GMFGEN.getGenCompartment(), GMFGEN.getGenCompartment_HideIfEmpty()); preserveIfSet(GMFGEN.getGenCompartment(), GMFGEN.getGenCompartment_NeedsTitle()); - - preserveIfSet(GMFGEN.getGenDiagram(), GMFGEN.getBatchValidation_ValidationEnabled()); - preserveIfSet(GMFGEN.getGenDiagram(), GMFGEN.getBatchValidation_ValidationDecorators()); - preserveIfSet(GMFGEN.getGenDiagram(), GMFGEN.getBatchValidation_ValidationDecoratorProviderClassName()); - preserveIfSet(GMFGEN.getGenDiagram(), GMFGEN.getBatchValidation_ValidationDecoratorProviderPriority()); - preserveIfSet(GMFGEN.getGenDiagram(), GMFGEN.getBatchValidation_ValidationProviderClassName()); - preserveIfSet(GMFGEN.getGenDiagram(), GMFGEN.getBatchValidation_ValidationProviderPriority()); - preserveIfSet(GMFGEN.getGenDiagram(), GMFGEN.getBatchValidation_MetricProviderPriority()); - preserveIfSet(GMFGEN.getGenDiagram(), GMFGEN.getBatchValidation_MetricProviderClassName()); + //if parent node is matched, then viemap is matched automatically because it is [1] feature. + //there are nothing to reconcile for viewmaps, all their properties are derived + //we need this only to dig into viewmap attributes + setMatcherForAllSubclasses(GMFGEN.getViewmap(), ALWAYS_MATCH); + + setMatcher(GMFGEN.getDefaultSizeAttributes(), ALWAYS_MATCH); + setCopier(GMFGEN.getDefaultSizeAttributes(), Copier.COMPLETE_COPY); + preserveIfSet(GMFGEN.getDefaultSizeAttributes(), GMFGEN.getDefaultSizeAttributes_Height()); + preserveIfSet(GMFGEN.getDefaultSizeAttributes(), GMFGEN.getDefaultSizeAttributes_Width()); } private Matcher getGenNodeMatcher(){ #P org.eclipse.gmf.tests Index: src/org/eclipse/gmf/tests/gen/CodegenReconcileTest.java =================================================================== RCS file: /cvsroot/technology/org.eclipse.gmf/tests/org.eclipse.gmf.tests/src/org/eclipse/gmf/tests/gen/CodegenReconcileTest.java,v retrieving revision 1.11 diff -u -r1.11 CodegenReconcileTest.java --- src/org/eclipse/gmf/tests/gen/CodegenReconcileTest.java 6 Jun 2006 17:48:30 -0000 1.11 +++ src/org/eclipse/gmf/tests/gen/CodegenReconcileTest.java 12 Jun 2006 19:59:24 -0000 @@ -25,12 +25,16 @@ import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.EcorePackage; import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.gmf.codegen.gmfgen.Attributes; +import org.eclipse.gmf.codegen.gmfgen.DefaultSizeAttributes; +import org.eclipse.gmf.codegen.gmfgen.GMFGenFactory; import org.eclipse.gmf.codegen.gmfgen.GMFGenPackage; import org.eclipse.gmf.codegen.gmfgen.GenCompartment; import org.eclipse.gmf.codegen.gmfgen.GenDiagram; import org.eclipse.gmf.codegen.gmfgen.GenEditorGenerator; import org.eclipse.gmf.codegen.gmfgen.GenNode; import org.eclipse.gmf.codegen.gmfgen.GenPlugin; +import org.eclipse.gmf.codegen.gmfgen.Viewmap; import org.eclipse.gmf.internal.codegen.GMFGenConfig; import org.eclipse.gmf.internal.common.reconcile.DefaultDecisionMaker; import org.eclipse.gmf.internal.common.reconcile.Reconciler; @@ -455,6 +459,77 @@ checkUserChange(new EditorChange(GMF.getGenEditorView_ID(), "my.editor.id")); } + public void testReconcileViewmapAttributes(){ + abstract class AbstractAttributesChange implements UserChange { + private int myAffectedViewmapsCount; + + protected abstract Attributes findAttributes(Viewmap viewmap); + protected abstract Attributes createUserAttributes(); + protected abstract void assertChanges(Attributes attributes); + + public final void applyChanges(GenEditorGenerator old) { + myAffectedViewmapsCount = 0; + for (Iterator allNodes = old.getDiagram().getAllNodes().iterator(); allNodes.hasNext();){ + GenNode next = (GenNode) allNodes.next(); + Viewmap nextViewmap = next.getViewmap(); + if (nextViewmap == null){ + continue; + } + Attributes attributes = findAttributes(nextViewmap); + assertNull("Reconciler is intended to work with attributes that are created only by user", attributes); + attributes = createUserAttributes(); + nextViewmap.getAttributes().add(attributes); + myAffectedViewmapsCount++; + } + assertTrue(myAffectedViewmapsCount > 0); + } + + public final void assertChangesPreserved(GenEditorGenerator current) { + int checkedViewmapsCount = 0; + for (Iterator allNodes = current.getDiagram().getAllNodes().iterator(); allNodes.hasNext();){ + GenNode next = (GenNode)allNodes.next(); + Viewmap nextViewmap = next.getViewmap(); + if (nextViewmap == null){ + continue; + } + Attributes attributes = findAttributes(nextViewmap); + assertNotNull(attributes); + assertChanges(attributes); + checkedViewmapsCount++; + } + assertEquals(myAffectedViewmapsCount, checkedViewmapsCount); + } + + public final ReconcilerConfigBase getReconcilerConfig() { + return new GMFGenConfig(); + } + } + + class DefaultSizeChange extends AbstractAttributesChange { + private static final int HEIGHT = 23; + private static final int WIDTH = 32; + + protected void assertChanges(Attributes attributes) { + DefaultSizeAttributes defaultSize = (DefaultSizeAttributes)attributes; + assertEquals(HEIGHT, defaultSize.getHeight()); + assertEquals(WIDTH, defaultSize.getWidth()); + } + + protected Attributes createUserAttributes() { + DefaultSizeAttributes defaultSize = GMFGenFactory.eINSTANCE.createDefaultSizeAttributes(); + defaultSize.setHeight(HEIGHT); + defaultSize.setWidth(WIDTH); + return defaultSize; + } + + protected Attributes findAttributes(Viewmap viewmap) { + return viewmap.find(DefaultSizeAttributes.class); + } + } + + checkUserChange(new DefaultSizeChange()); + } + private void checkUserChange(UserChange userChange){ GenEditorGenerator old = createCopy(); GenEditorGenerator current = createCopy(); Index: src/org/eclipse/gmf/tests/AllTests.java =================================================================== RCS file: /cvsroot/technology/org.eclipse.gmf/tests/org.eclipse.gmf.tests/src/org/eclipse/gmf/tests/AllTests.java,v retrieving revision 1.51 diff -u -r1.51 AllTests.java --- src/org/eclipse/gmf/tests/AllTests.java 7 Jun 2006 09:52:28 -0000 1.51 +++ src/org/eclipse/gmf/tests/AllTests.java 12 Jun 2006 19:59:23 -0000 @@ -32,6 +32,7 @@ import org.eclipse.gmf.tests.gen.LabelSupportTest; import org.eclipse.gmf.tests.gen.MapModeStrategyTest; import org.eclipse.gmf.tests.gen.RTFigureTest; +import org.eclipse.gmf.tests.gen.ReconcilerPoolTest; import org.eclipse.gmf.tests.gen.ShapePropertiesTest; import org.eclipse.gmf.tests.gen.StandaloneMapModeTest; import org.eclipse.gmf.tests.gen.StandalonePluginConverterTest; @@ -103,6 +104,7 @@ suite.addTest(feed(AuditRulesTest.class, sessionSetup2)); suite.addTest(feed(ElementInitializerTest.class, sessionSetup2)); suite.addTest(feed(CodegenReconcileTest.class, compartmentsSession)); + suite.addTestSuite(ReconcilerPoolTest.class); // fires new runtime workbench initialization suite.addTestSuite(CompilationTest.class); Index: src/org/eclipse/gmf/tests/gen/ReconcilerPoolTest.java =================================================================== RCS file: src/org/eclipse/gmf/tests/gen/ReconcilerPoolTest.java diff -N src/org/eclipse/gmf/tests/gen/ReconcilerPoolTest.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/gmf/tests/gen/ReconcilerPoolTest.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2006 Borland Software Corporation + * + * 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 + * + * Contributors: + * Michael Golubev (Borland) - initial API and implementation + */ + +package org.eclipse.gmf.tests.gen; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.eclipse.gmf.internal.common.reconcile.AbstractPool; + +import junit.framework.TestCase; + +public class ReconcilerPoolTest extends TestCase { + public void testMoreAcquiresThanCapacity(){ + Pool pool = new Pool(2); + Object a = pool.acquire(); + Object b = pool.acquire(); + Object c = pool.acquire(); + pool.release(a); + pool.release(b); + pool.release(c); + + assertEquals(0, pool.getNullsCount()); + assertEquals(3, pool.getCreationCount()); + } + + public void testMuchMoreAcquiresGoodShaped(){ + Pool pool = new Pool(2); + Object a = pool.acquire(); + Object b = pool.acquire(); + + final int NOT_POOLED_AQUIRES = 50; + for (int i = 0; i < NOT_POOLED_AQUIRES; i++){ + Object next = pool.acquire(); + pool.release(next); + } + + pool.release(a); + pool.release(b); + + assertEquals("When first NOT_POOLED object is release it should be returned to pool", 1 + 2, pool.getCreationCount()); + assertEquals(0, pool.getNullsCount()); + } + + public void testImmediateRelease(){ + Pool pool = new Pool(2); + for (int i = 0; i < 50; i++){ + Object next = pool.acquire(); + assertEquals(pool.capacity(), pool.getNullsCount()); + pool.release(next); + } + assertEquals(1, pool.getCreationCount()); + assertEquals(pool.capacity() - 1, pool.getNullsCount()); + + for (int i = 0; i < 50; i++){ + Object nextA = pool.acquire(); + Object nextB = pool.acquire(); + + assertEquals(pool.capacity(), pool.getNullsCount()); + + if (i % 2 == 0){ + pool.release(nextA); + pool.release(nextB); + } else { + pool.release(nextB); + pool.release(nextA); + } + } + + assertEquals(2, pool.getCreationCount()); + assertEquals(pool.capacity() - 2, pool.getNullsCount()); + } + + public void testBadCase(){ + Pool pool = new Pool(2); + final int ACQUIRES = 50; + + List allAcquired = new LinkedList(); + for (int i = 0; i < ACQUIRES; i++){ + allAcquired.add(pool.acquire()); + } + for (Iterator it = allAcquired.iterator(); it.hasNext();){ + pool.release(it.next()); + it.remove(); + } + assertEquals(ACQUIRES, pool.getCreationCount()); + assertEquals(0, pool.getNullsCount()); + + //once again + for (int i = 0; i < ACQUIRES; i++){ + allAcquired.add(pool.acquire()); + } + for (Iterator it = allAcquired.iterator(); it.hasNext();){ + pool.release(it.next()); + it.remove(); + } + + assertEquals(ACQUIRES * 2 - pool.capacity(), pool.getCreationCount()); + assertEquals(0, pool.getNullsCount()); + } + + public void testBaseInternals(){ + Pool pool = new Pool(2); + Object a = pool.acquire(); + assertEquals(1, pool.getCreationCount()); + assertEquals(2, pool.getNullsCount()); + + pool.release(a); + assertEquals(1, pool.getCreationCount()); + assertEquals(1, pool.getNullsCount()); + + Object aa = pool.acquire(); + assertEquals(1, pool.getCreationCount()); + assertEquals(2, pool.getNullsCount()); + assertSame(a, aa); + + Object b = pool.acquire(); + assertEquals(2, pool.getCreationCount()); + assertEquals(2, pool.getNullsCount()); + assertNotSame(a, b); + + pool.release(a); + assertEquals(2, pool.getCreationCount()); + assertEquals(1, pool.getNullsCount()); + + pool.release(b); + assertEquals(2, pool.getCreationCount()); + assertEquals(0, pool.getNullsCount()); + } + + private static class Pool extends AbstractPool { + private int myCreationCount; + + public Pool(int capacity) { + super(capacity); + } + + protected Object createNew() { + myCreationCount++; + return new Object(); + } + + public Object acquire(){ + Object result = internalAcquire(); + assertNotNull(result); + return result; + } + + public void release(Object object){ + internalRelease(object); + } + + public int getCreationCount() { + return myCreationCount; + } + + public int getNullsCount() { + return internalCountNulls(); + } + } +} #P org.eclipse.gmf.common Index: src/org/eclipse/gmf/internal/common/reconcile/Reconciler.java =================================================================== RCS file: /cvsroot/technology/org.eclipse.gmf/plugins/org.eclipse.gmf.common/src/org/eclipse/gmf/internal/common/reconcile/Reconciler.java,v retrieving revision 1.2 diff -u -r1.2 Reconciler.java --- src/org/eclipse/gmf/internal/common/reconcile/Reconciler.java 15 Mar 2006 15:16:59 -0000 1.2 +++ src/org/eclipse/gmf/internal/common/reconcile/Reconciler.java 12 Jun 2006 19:59:25 -0000 @@ -12,18 +12,36 @@ package org.eclipse.gmf.internal.common.reconcile; +import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; public class Reconciler { + /** + * Expected max breadth of the node in the reconciled tree + */ + private static final int PAIRS_POOL_SIZE = 100; + + /** + * Expected depth of the reconciled tree + */ + private static final int STORAGE_POOL_SIZE = 10; + private final ReconcilerConfig myConfig; + private final MatchingSession myMatchingSession; + private final StoragePool myStoragePool; public Reconciler(ReconcilerConfig config){ myConfig = config; + myMatchingSession = new MatchingSession(new PairsPool(PAIRS_POOL_SIZE)); + myStoragePool = new StoragePool(STORAGE_POOL_SIZE); } protected void handleNotMatchedCurrent(EObject current){ @@ -31,13 +49,18 @@ //FIXME How to handle not macthed old ??? } + protected EObject handleNotMatchedOld(EObject currentParent, EObject notMatchedOld) { + Copier copier = myConfig.getCopier(notMatchedOld.eClass()); + return copier.copyToCurrent(currentParent, notMatchedOld); + } + public void reconcileResource(Resource current, Resource old){ - reconcileContents(current.getContents(), old.getContents()); + reconcileContents(null, current.getContents(), old.getContents()); } public void reconcileTree(EObject currentRoot, EObject oldRoot){ reconcileVertex(currentRoot, oldRoot); - reconcileContents(currentRoot.eContents(), oldRoot.eContents()); + reconcileContents(currentRoot, currentRoot.eContents(), oldRoot.eContents()); } private void reconcileVertex(EObject current, EObject old){ @@ -50,31 +73,159 @@ } } - private void reconcileContents(Collection allCurrents, Collection allOlds){ - for (Iterator currentContents = allCurrents.iterator(); currentContents.hasNext();){ - EObject nextCurrent = (EObject) currentContents.next(); - EObject nextOld = findMatched(nextCurrent, allOlds); - if (nextOld == null){ - handleNotMatchedCurrent(nextCurrent); - } else { + private void reconcileContents(EObject currentParent, Collection allCurrents, Collection allOlds){ + if (allCurrents.isEmpty() && allOlds.isEmpty()){ + return; + } + List storage = myStoragePool.acquireList(); + myMatchingSession.match(allCurrents, allOlds, storage); + + for (Iterator pairs = storage.iterator(); pairs.hasNext();){ + Pair next = (Pair)pairs.next(); + EObject nextCurrent = next.current; + EObject nextOld = next.old; + assert (nextCurrent != null || nextOld != null); + + myMatchingSession.releasePair(next); + + if (nextCurrent == null){ + if (currentParent != null){ //never copy top-level resource contents + nextCurrent = handleNotMatchedOld(currentParent, nextOld); + } + } + + if (nextCurrent != null && nextOld != null){ reconcileTree(nextCurrent, nextOld); + } else if (nextOld == null){ + handleNotMatchedCurrent(nextCurrent); } } + myStoragePool.release(storage); } - private EObject findMatched(EObject current, Collection allOld){ - EClass eClass = current.eClass(); - Matcher matcher = myConfig.getMatcher(eClass); - EObject result = null; - if (matcher != Matcher.FALSE){ - for (Iterator all = allOld.iterator(); result == null && all.hasNext();){ - EObject next = (EObject)all.next(); - if (eClass.equals(next.eClass()) && matcher.match(current, next)){ - result = next; + private static class Pair { + public EObject current; + public EObject old; + + public void reset(){ + current = null; + old = null; + } + } + + private class MatchingSession { + private final Collection myCurrents; + private final Collection myOlds; + private final PairsPool myPool; + private boolean myIsMatching; + + public MatchingSession(PairsPool pool){ + myPool = pool; + myCurrents = new LinkedList(); + myOlds = new HashSet(); + } + + public void match(Collection currents, Collection olds, Collection output){ + assert !myIsMatching; + assert myOlds.isEmpty(); + assert myCurrents.isEmpty(); + + if (myIsMatching){ + throw new IllegalStateException("FIXME: remove me"); + } + + try { + myIsMatching = true; + + myCurrents.addAll(currents); + myOlds.addAll(olds); + + for (Iterator currentContents = myCurrents.iterator(); !myOlds.isEmpty() && currentContents.hasNext();){ + EObject nextCurrent = (EObject) currentContents.next(); + Pair nextPair = acquirePair(); + nextPair.current = nextCurrent; + nextPair.old = removeMatched(nextCurrent, myOlds); + output.add(nextPair); + currentContents.remove(); + } + + for (Iterator notMatchedOlds = myOlds.iterator(); notMatchedOlds.hasNext();){ + Pair nextPair = acquirePair(); + nextPair.current = null; + nextPair.old = (EObject)notMatchedOlds.next(); + output.add(nextPair); } + } finally { + myIsMatching = false; + myCurrents.clear(); + myOlds.clear(); } } - return result; + + private EObject removeMatched(EObject current, Collection allOld){ + EClass eClass = current.eClass(); + Matcher matcher = myConfig.getMatcher(eClass); + EObject result = null; + if (matcher != Matcher.FALSE){ + for (Iterator all = allOld.iterator(); all.hasNext();){ + EObject next = (EObject)all.next(); + if (eClass.equals(next.eClass()) && matcher.match(current, next)){ + result = next; + all.remove(); + break; + } + } + } + return result; + } + + private Pair acquirePair(){ + return myPool.acquire(); + } + + public void releasePair(Pair pair){ + myPool.release(pair); + } + + } + + private static class PairsPool extends AbstractPool { + public PairsPool(int capacity) { + super(capacity); + } + + public Pair acquire(){ + return (Pair)internalAcquire(); + } + + public void release(Pair pair){ + pair.current = null; + pair.old = null; + internalRelease(pair); + } + + protected Object createNew() { + return new Pair(); + } + } + + private static class StoragePool extends AbstractPool { + public StoragePool(int capacity){ + super(capacity); + } + + public List acquireList(){ + return (List)internalAcquire(); + } + + public void release(List list){ + list.clear(); + internalRelease(list); + } + + protected Object createNew() { + return new ArrayList(); + } } } Index: src/org/eclipse/gmf/internal/common/reconcile/ReconcilerConfigBase.java =================================================================== RCS file: /cvsroot/technology/org.eclipse.gmf/plugins/org.eclipse.gmf.common/src/org/eclipse/gmf/internal/common/reconcile/ReconcilerConfigBase.java,v retrieving revision 1.2 diff -u -r1.2 ReconcilerConfigBase.java --- src/org/eclipse/gmf/internal/common/reconcile/ReconcilerConfigBase.java 15 Mar 2006 15:16:59 -0000 1.2 +++ src/org/eclipse/gmf/internal/common/reconcile/ReconcilerConfigBase.java 12 Jun 2006 19:59:25 -0000 @@ -14,6 +14,7 @@ import java.text.MessageFormat; import java.util.HashMap; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -25,15 +26,21 @@ public class ReconcilerConfigBase implements ReconcilerConfig { private static final EClassRecord EMPTY_RECORD = new EClassRecord(); private final HashMap myEClass2Record; + private final HashMap myAbstractEClass2SubclassesRecord; public ReconcilerConfigBase(){ myEClass2Record = new HashMap(); + myAbstractEClass2SubclassesRecord = new HashMap(); } public final Matcher getMatcher(EClass eClass) { return getRecord(eClass, false).getMatcher(); } + public Copier getCopier(EClass eClass) { + return getRecord(eClass, false).getCopier(); + } + public final DecisionMaker[] getDecisionMakers(EClass eClass) { return getRecord(eClass, false).getDecisionMakers(); } @@ -42,6 +49,19 @@ getRecord(eClass, true).setMatcher(matcher); } + protected final void setCopier(EClass eClass, Copier copier){ + getRecord(eClass, true).setCopier(copier); + } + + protected final void setMatcherForAllSubclasses(EClass eClass, Matcher matcher){ + if (!eClass.isAbstract()){ + throw new IllegalArgumentException( + "This is not safe method that may lead to strange behaviour in case of multiple inheritance. " + + "We tried to limit its usage as much as possible"); + } + getTemplateRecord(eClass, true).setMatcher(matcher); + } + protected final void addDecisionMaker(EClass eClass, DecisionMaker decisionMaker){ getRecord(eClass, true).addDecisionMaker(decisionMaker); } @@ -69,11 +89,29 @@ myEClass2Record.put(eClass, result); } else { result = EMPTY_RECORD; + for (Iterator superClasses = eClass.getEAllSuperTypes().iterator(); result == EMPTY_RECORD && superClasses.hasNext();){ + EClass nextSuper = (EClass) superClasses.next(); + result = getTemplateRecord(nextSuper, false); + } + if (result != EMPTY_RECORD){ + //cache it for the next time + myEClass2Record.put(eClass, result); + } } } return result; } + private EClassRecord getTemplateRecord(EClass abstractSuperClass, boolean force){ + assert abstractSuperClass.isAbstract(); + EClassRecord result = (EClassRecord)myAbstractEClass2SubclassesRecord.get(abstractSuperClass); + if (result == null && force){ + result = new EClassRecord(); + myAbstractEClass2SubclassesRecord.put(abstractSuperClass, result); + } + return result == null ? EMPTY_RECORD : result; + } + private void checkStructuralFeature(EClass expectedClass, EAttribute feature) { if (expectedClass.getEStructuralFeature(feature.getFeatureID()) != feature){ throw new IllegalArgumentException(MessageFormat.format("Alien feature {0} for EClass {1}", new Object[] {feature, expectedClass})); @@ -88,6 +126,7 @@ private static class EClassRecord { private Matcher myMatcher = Matcher.FALSE; + private Copier myCopier = Copier.NEVER_COPY; private final List myDecisionMakers = new LinkedList(); private DecisionMaker[] myMakersArray; @@ -96,6 +135,10 @@ makersSetChanged(); } + public void setCopier(Copier copier) { + myCopier = copier; + } + public DecisionMaker[] getDecisionMakers(){ if (myMakersArray == null){ myMakersArray = (DecisionMaker[]) myDecisionMakers.toArray(new DecisionMaker[myDecisionMakers.size()]); @@ -111,6 +154,10 @@ return myMatcher; } + public Copier getCopier() { + return myCopier; + } + private void makersSetChanged(){ myMakersArray = null; } Index: src/org/eclipse/gmf/internal/common/reconcile/ReconcilerConfig.java =================================================================== RCS file: /cvsroot/technology/org.eclipse.gmf/plugins/org.eclipse.gmf.common/src/org/eclipse/gmf/internal/common/reconcile/ReconcilerConfig.java,v retrieving revision 1.1 diff -u -r1.1 ReconcilerConfig.java --- src/org/eclipse/gmf/internal/common/reconcile/ReconcilerConfig.java 13 Mar 2006 12:02:47 -0000 1.1 +++ src/org/eclipse/gmf/internal/common/reconcile/ReconcilerConfig.java 12 Jun 2006 19:59:25 -0000 @@ -17,4 +17,5 @@ public interface ReconcilerConfig { Matcher getMatcher(EClass eClass); DecisionMaker[] getDecisionMakers(EClass eClass); + Copier getCopier(EClass eClass); } Index: src/org/eclipse/gmf/internal/common/reconcile/Copier.java =================================================================== RCS file: src/org/eclipse/gmf/internal/common/reconcile/Copier.java diff -N src/org/eclipse/gmf/internal/common/reconcile/Copier.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/gmf/internal/common/reconcile/Copier.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2006 Borland Software Corporation + * + * 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 + * + * Contributors: + * Michael Golubev (Borland) - initial API and implementation + */ + +package org.eclipse.gmf.internal.common.reconcile; + +import java.util.Collection; +import java.util.Collections; + +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; + +public interface Copier { + /** + * Copies relevant content of the old object into the current ancestor. + * + * @param currentParent + * the parent node in the current tree. It is guarranteed that + * parent was matched with old.eContainer() + * @param old + * the element found in the old tree which does not have matched + * element in the current tree + * @return just created element copied into the current tree or + * null if copier decided to avoid creation of + * element. + * + * NOTE: in case if this method returns true, result is considered as + * "matched" with old automatically, without any further + * matchings + */ + public EObject copyToCurrent(EObject currentParent, EObject old); + + public static final Copier NEVER_COPY = new Copier(){ + public EObject copyToCurrent(EObject currentParent, EObject old) { + return null; + } + }; + + public static final Copier COMPLETE_COPY = new Copier(){ + + public EObject copyToCurrent(EObject currentParent, EObject old) { + safetyCheck(old); + EClass currentParentEClass = currentParent.eClass(); + EObject oldParent = old.eContainer(); + EClass oldParentEClass = oldParent.eClass(); + EObject currentCopy = null; + if (currentParentEClass.equals(oldParentEClass)){ + currentCopy = EcoreUtil.copy(old); + EStructuralFeature containment = old.eContainingFeature(); + Object currentValue = currentParent.eGet(containment); + if (currentValue instanceof Collection){ + ((Collection)currentValue).add(currentCopy); + } else { + currentParent.eSet(containment, currentCopy); + } + } + return currentCopy; + } + + /** + * It is not trivial to copy the external references in the context of + * reconilation. If you need this, do not use this simple implementation. + */ + private void safetyCheck(EObject old){ + if (!EcoreUtil.CrossReferencer.find(Collections.singleton(old)).isEmpty()){ + throw new IllegalArgumentException("I am not intended to copy elements woth cross references"); + } + } + + }; + +} Index: src/org/eclipse/gmf/internal/common/reconcile/AbstractPool.java =================================================================== RCS file: src/org/eclipse/gmf/internal/common/reconcile/AbstractPool.java diff -N src/org/eclipse/gmf/internal/common/reconcile/AbstractPool.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/gmf/internal/common/reconcile/AbstractPool.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2006 Borland Software Corporation + * + * 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 + * + * Contributors: + * Michael Golubev (Borland) - initial API and implementation + */ + +package org.eclipse.gmf.internal.common.reconcile; + +public abstract class AbstractPool { + private final Object[] myPairs; + private int myNextIndex; + + protected abstract Object createNew(); + + public AbstractPool(int capacity){ + myPairs = new Object[capacity]; + } + + public final int capacity(){ + return myPairs.length; + } + + protected final Object internalAcquire(){ + Object result = null; + if (myNextIndex < myPairs.length){ + result = myPairs[myNextIndex]; + myPairs[myNextIndex] = null; + myNextIndex++; + } + if (result == null){ + result = createNew(); + } + return result; + } + + protected final void internalRelease(Object pair){ + if (myNextIndex > 0){ + myNextIndex--; + if (myPairs[myNextIndex] != null){ + throw new IllegalStateException("FIXME: should be assert here"); + } + myPairs[myNextIndex] = pair; + } + } + + /** + * For testing purposes only + */ + protected final int internalCountNulls(){ + int result = 0; + for (int i = 0; i < myPairs.length; i++){ + if (myPairs[i] == null){ + result++; + } + } + return result; + } +}