From 9cabab095ea19a9d134bcb3770f5f9ebbaa8a586 Mon Sep 17 00:00:00 2001 From: Darin Wright Date: Fri, 24 Sep 2010 19:43:15 +0000 Subject: Bug 326063 - org.eclipse.debug.core.IExpressionManager.getExpressions() is not thread safe that it causes java.lang.ArrayIndexOutOfBoundsException --- .../core/org/eclipse/debug/core/DebugPlugin.java | 3 + .../debug/internal/core/ExpressionManager.java | 195 +++---- .../debug/internal/core/WatchExpression.java | 10 +- .../tests/expressions/ExpressionManagerTests.java | 569 +++++++++++++++++++++ 4 files changed, 684 insertions(+), 93 deletions(-) create mode 100644 org.eclipse.debug.tests/src/org/eclipse/debug/tests/expressions/ExpressionManagerTests.java diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/core/DebugPlugin.java b/org.eclipse.debug.core/core/org/eclipse/debug/core/DebugPlugin.java index 5349df649..308bcfb72 100644 --- a/org.eclipse.debug.core/core/org/eclipse/debug/core/DebugPlugin.java +++ b/org.eclipse.debug.core/core/org/eclipse/debug/core/DebugPlugin.java @@ -641,6 +641,9 @@ public class DebugPlugin extends Plugin { ResourcesPlugin.getWorkspace().addSaveParticipant(getUniqueIdentifier(), new ISaveParticipant() { public void saving(ISaveContext saveContext) throws CoreException { + if (fExpressionManager != null) { + fExpressionManager.storeWatchExpressions(); + } Preferences.savePreferences(DebugPlugin.getUniqueIdentifier()); } public void rollback(ISaveContext saveContext) {} diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/ExpressionManager.java b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/ExpressionManager.java index 17d3b6106..5b0360c12 100644 --- a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/ExpressionManager.java +++ b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/ExpressionManager.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2009 IBM Corporation and others. + * Copyright (c) 2000, 2010 IBM Corporation 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 @@ -263,33 +263,41 @@ public class ExpressionManager extends PlatformObject implements IExpressionMana * @see org.eclipse.debug.core.IExpressionManager#addExpressions(org.eclipse.debug.core.model.IExpression[]) */ public void addExpressions(IExpression[] expressions) { - if (fExpressions == null) { - fExpressions = new Vector(expressions.length); - } - boolean addedWatchExpression= false; - List added = new ArrayList(expressions.length); - for (int i = 0; i < expressions.length; i++) { - IExpression expression = expressions[i]; - if (fExpressions.indexOf(expression) == -1) { - added.add(expression); - fExpressions.add(expression); - if (expression instanceof IWatchExpression) { - addedWatchExpression= true; - } - } - } + List added = doAdd(expressions); if (!added.isEmpty()) { fireUpdate((IExpression[])added.toArray(new IExpression[added.size()]), ADDED); } - if (addedWatchExpression) { - storeWatchExpressions(); + } + + /** + * Adds the given expressions to the list of managed expressions, and returns a list + * of expressions that were actually added. Expressions that already exist in the + * managed list are not added. + * + * @param expressions expressions to add + * @return list of expressions that were actually added. + */ + private List doAdd(IExpression[] expressions) { + List added = new ArrayList(expressions.length); + synchronized (this) { + if (fExpressions == null) { + fExpressions = new Vector(expressions.length); + } + for (int i = 0; i < expressions.length; i++) { + IExpression expression = expressions[i]; + if (fExpressions.indexOf(expression) == -1) { + added.add(expression); + fExpressions.add(expression); + } + } } + return added; } /* (non-Javadoc) * @see org.eclipse.debug.core.IExpressionManager#getExpressions() */ - public IExpression[] getExpressions() { + public synchronized IExpression[] getExpressions() { if (fExpressions == null) { return new IExpression[0]; } @@ -301,7 +309,7 @@ public class ExpressionManager extends PlatformObject implements IExpressionMana /* (non-Javadoc) * @see org.eclipse.debug.core.IExpressionManager#getExpressions(java.lang.String) */ - public IExpression[] getExpressions(String modelIdentifier) { + public synchronized IExpression[] getExpressions(String modelIdentifier) { if (fExpressions == null) { return new IExpression[0]; } @@ -331,38 +339,37 @@ public class ExpressionManager extends PlatformObject implements IExpressionMana * @since 3.4 */ public void insertExpressions(IExpression[] expressions, IExpression insertionLocation, boolean insertBefore){ - if (fExpressions == null) { - addExpressions(expressions); - return; - } - - int insertionIndex = fExpressions.indexOf(insertionLocation); - if (insertionIndex < 0){ - addExpressions(expressions); - return; - } - if (!insertBefore){ - insertionIndex++; - } - boolean addedWatchExpression = false; - List added = new ArrayList(expressions.length); - for (int i = 0; i < expressions.length; i++) { - IExpression expression = expressions[i]; - if (fExpressions.indexOf(expression) == -1) { - //Insert in the same order as the array is passed - fExpressions.add(insertionIndex+added.size(), expression); - added.add(expression); - if (expression instanceof IWatchExpression) { - addedWatchExpression= true; + List added = null; + List inserted = null; + int insertionIndex = -1; + synchronized (this) { + if (fExpressions == null || ((insertionIndex = fExpressions.indexOf(insertionLocation)) < 0)) { + added = doAdd(expressions); + } else { + if (!insertBefore){ + insertionIndex++; } - } + inserted = new ArrayList(expressions.length); + for (int i = 0; i < expressions.length; i++) { + IExpression expression = expressions[i]; + if (fExpressions.indexOf(expression) == -1) { + //Insert in the same order as the array is passed + fExpressions.add(insertionIndex+inserted.size(), expression); + inserted.add(expression); + } + } + } } - - if (!added.isEmpty()) { - fireUpdate((IExpression[])added.toArray(new IExpression[added.size()]), INSERTED, insertionIndex); + if (added != null) { + if (!added.isEmpty()) { + fireUpdate((IExpression[])added.toArray(new IExpression[added.size()]), ADDED); + } + return; } - if (addedWatchExpression) { - storeWatchExpressions(); + if (inserted != null) { + if (!inserted.isEmpty()) { + fireUpdate((IExpression[])inserted.toArray(new IExpression[inserted.size()]), INSERTED, insertionIndex); + } } } @@ -379,37 +386,40 @@ public class ExpressionManager extends PlatformObject implements IExpressionMana * @since 3.4 */ public void moveExpressions(IExpression[] expressions, IExpression insertionLocation, boolean insertBefore){ - if (fExpressions == null){ - return; - } - int insertionIndex = fExpressions.indexOf(insertionLocation); - if (insertionIndex < 0){ - return; - } - if (!insertBefore){ - insertionIndex++; - } - List movedExpressions = new ArrayList(expressions.length); - for (int i = 0; i < expressions.length; i++) { - int removeIndex = fExpressions.indexOf(expressions[i]); - if (removeIndex >= 0){ - movedExpressions.add(expressions[i]); - if (removeIndex < insertionIndex){ - insertionIndex--; + int insertionIndex = -1; + IExpression[] movedExpressionsArray = null; + synchronized (this) { + if (fExpressions == null){ + return; + } + insertionIndex = fExpressions.indexOf(insertionLocation); + if (insertionIndex < 0){ + return; + } + if (!insertBefore){ + insertionIndex++; + } + + for (int i = 0; i < expressions.length; i++) { + int removeIndex = fExpressions.indexOf(expressions[i]); + if (removeIndex >= 0){ + movedExpressions.add(expressions[i]); + if (removeIndex < insertionIndex){ + insertionIndex--; + } + fExpressions.remove(removeIndex); } - fExpressions.remove(removeIndex); } - } - IExpression[] movedExpressionsArray = (IExpression[])movedExpressions.toArray(new IExpression[movedExpressions.size()]); - for (int i = 0; i < movedExpressionsArray.length; i++) { - // Insert the expressions in the same order as the passed array - fExpressions.add(insertionIndex+i,movedExpressionsArray[i]); + movedExpressionsArray = (IExpression[])movedExpressions.toArray(new IExpression[movedExpressions.size()]); + for (int i = 0; i < movedExpressionsArray.length; i++) { + // Insert the expressions in the same order as the passed array + fExpressions.add(insertionIndex+i,movedExpressionsArray[i]); + } } if (!movedExpressions.isEmpty()) { fireUpdate(movedExpressionsArray, MOVED, insertionIndex); - storeWatchExpressions(); } } @@ -424,20 +434,25 @@ public class ExpressionManager extends PlatformObject implements IExpressionMana * @see org.eclipse.debug.core.IExpressionManager#removeExpressions(org.eclipse.debug.core.model.IExpression[]) */ public void removeExpressions(IExpression[] expressions) { - if (fExpressions == null) { - return; - } List removed = new ArrayList(expressions.length); - for (int i = 0; i < expressions.length; i++) { - IExpression expression = expressions[i]; - if (fExpressions.remove(expression)) { - removed.add(expression); - expression.dispose(); - } + synchronized (this) { + if (fExpressions == null) { + return; + } + for (int i = 0; i < expressions.length; i++) { + IExpression expression = expressions[i]; + if (fExpressions.remove(expression)) { + removed.add(expression); + } + } + } + // dispose outside of the synchronized block + Iterator iterator = removed.iterator(); + while (iterator.hasNext()) { + ((IExpression) iterator.next()).dispose(); } if (!removed.isEmpty()) { fireUpdate((IExpression[])removed.toArray(new IExpression[removed.size()]), REMOVED); - storeWatchExpressions(); } } @@ -468,11 +483,14 @@ public class ExpressionManager extends PlatformObject implements IExpressionMana * @param expression the changed expression * @param persist whether to persist the expressions */ - protected void watchExpressionChanged(IWatchExpression expression, boolean persist) { - if (fExpressions != null && fExpressions.contains(expression)) { - if (persist) { - storeWatchExpressions(); + protected void watchExpressionChanged(IWatchExpression expression) { + boolean notify = false; + synchronized (this) { + if (fExpressions != null && fExpressions.contains(expression)) { + notify = true; } + } + if (notify) { fireUpdate(new IExpression[]{expression}, CHANGED); } } @@ -505,7 +523,7 @@ public class ExpressionManager extends PlatformObject implements IExpressionMana /* (non-Javadoc) * @see org.eclipse.debug.core.IExpressionManager#hasExpressions() */ - public boolean hasExpressions() { + public synchronized boolean hasExpressions() { return fExpressions != null && !fExpressions.isEmpty(); } @@ -557,6 +575,7 @@ public class ExpressionManager extends PlatformObject implements IExpressionMana public void run() throws Exception { switch (fType) { case ADDED: + case INSERTED: fListener.expressionAdded(fExpression); break; case REMOVED: diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/WatchExpression.java b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/WatchExpression.java index ed51fc9e6..8be2ba3b4 100644 --- a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/WatchExpression.java +++ b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/WatchExpression.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2009 IBM Corporation and others. + * Copyright (c) 2000, 2010 IBM Corporation 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 @@ -149,8 +149,8 @@ public class WatchExpression implements IWatchExpression { * * @param persist whether to persist the expression */ - private void watchExpressionChanged(boolean persist) { - ((ExpressionManager)DebugPlugin.getDefault().getExpressionManager()).watchExpressionChanged(this, persist); + private void watchExpressionChanged() { + ((ExpressionManager)DebugPlugin.getDefault().getExpressionManager()).watchExpressionChanged(this); } /** @@ -233,7 +233,7 @@ public class WatchExpression implements IWatchExpression { */ public void setEnabled(boolean enabled) { fEnabled= enabled; - watchExpressionChanged(true); + watchExpressionChanged(); evaluate(); } @@ -242,7 +242,7 @@ public class WatchExpression implements IWatchExpression { */ public void setExpressionText(String expression) { fExpressionText= expression; - watchExpressionChanged(true); + watchExpressionChanged(); evaluate(); } diff --git a/org.eclipse.debug.tests/src/org/eclipse/debug/tests/expressions/ExpressionManagerTests.java b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/expressions/ExpressionManagerTests.java new file mode 100644 index 000000000..927632bcc --- /dev/null +++ b/org.eclipse.debug.tests/src/org/eclipse/debug/tests/expressions/ExpressionManagerTests.java @@ -0,0 +1,569 @@ +/******************************************************************************* + * Copyright (c) 2010 IBM Corporation 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 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.tests.expressions; + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.IExpressionListener; +import org.eclipse.debug.core.IExpressionManager; +import org.eclipse.debug.core.IExpressionsListener; +import org.eclipse.debug.core.model.IExpression; +import org.eclipse.debug.core.model.IWatchExpression; +import org.eclipse.debug.internal.core.ExpressionManager; +import org.eclipse.debug.internal.core.IExpressionsListener2; + +/** + * Tests expression manager and listener call backs + */ +public class ExpressionManagerTests extends TestCase { + + class SinlgeListener implements IExpressionListener { + + List added = new ArrayList(); + List removed = new ArrayList(); + List changed = new ArrayList(); + int addedCallbacks = 0; + int removedCallbacks = 0; + int changedCallbacks = 0; + + /* (non-Javadoc) + * @see org.eclipse.debug.core.IExpressionListener#expressionAdded(org.eclipse.debug.core.model.IExpression) + */ + public void expressionAdded(IExpression expression) { + added.add(expression); + addedCallbacks++; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.IExpressionListener#expressionRemoved(org.eclipse.debug.core.model.IExpression) + */ + public void expressionRemoved(IExpression expression) { + removed.add(expression); + removedCallbacks++; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.IExpressionListener#expressionChanged(org.eclipse.debug.core.model.IExpression) + */ + public void expressionChanged(IExpression expression) { + changed.add(expression); + changedCallbacks++; + } + + } + + class MultiListener implements IExpressionsListener { + + List added = new ArrayList(); + List removed = new ArrayList(); + List changed = new ArrayList(); + int addedCallbacks = 0; + int removedCallbacks = 0; + int changedCallbacks = 0; + + /* (non-Javadoc) + * @see org.eclipse.debug.core.IExpressionsListener#expressionsAdded(org.eclipse.debug.core.model.IExpression[]) + */ + public void expressionsAdded(IExpression[] expressions) { + for (int i = 0; i < expressions.length; i++) { + added.add(expressions[i]); + } + addedCallbacks++; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.IExpressionsListener#expressionsRemoved(org.eclipse.debug.core.model.IExpression[]) + */ + public void expressionsRemoved(IExpression[] expressions) { + for (int i = 0; i < expressions.length; i++) { + removed.add(expressions[i]); + } + removedCallbacks++; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.core.IExpressionsListener#expressionsChanged(org.eclipse.debug.core.model.IExpression[]) + */ + public void expressionsChanged(IExpression[] expressions) { + for (int i = 0; i < expressions.length; i++) { + changed.add(expressions[i]); + } + changedCallbacks++; + } + + } + + class InsertMoveListener extends MultiListener implements IExpressionsListener2 { + + List moved = new ArrayList(); + List inserted = new ArrayList(); + int insertIndex = -1; + int movedCallbacks = 0; + int insertedCallbacks = 0; + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.core.IExpressionsListener2#expressionsMoved(org.eclipse.debug.core.model.IExpression[], int) + */ + public void expressionsMoved(IExpression[] expressions, int index) { + for (int i = 0; i < expressions.length; i++) { + moved.add(expressions[i]); + } + movedCallbacks++; + insertIndex = index; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.core.IExpressionsListener2#expressionsInserted(org.eclipse.debug.core.model.IExpression[], int) + */ + public void expressionsInserted(IExpression[] expressions, int index) { + for (int i = 0; i < expressions.length; i++) { + inserted.add(expressions[i]); + } + insertedCallbacks++; + insertIndex = index; + } + + } + + /** + * Returns the expression manager. + * + * @return expression manager + */ + protected IExpressionManager getManager() { + return DebugPlugin.getDefault().getExpressionManager(); + } + + /* (non-Javadoc) + * @see junit.framework.TestCase#tearDown() + */ + protected void tearDown() throws Exception { + // remove all expressions from the manager + super.tearDown(); + getManager().removeExpressions(getManager().getExpressions()); + } + + /** + * Returns the index of the given expression in the given list or -1 if not present. + * + * @param expression candidate + * @param list list to search + * @return index or -1 + */ + private int indexOf(IExpression expression, IExpression[] list) { + for (int i = 0; i < list.length; i++) { + if (expression.equals(list[i])) { + return i; + } + } + return -1; + } + + /** + * Add expressions and ensure proper call backs are received. + */ + public void testAddExpressions() { + IExpressionManager manager = getManager(); + SinlgeListener single = new SinlgeListener(); + MultiListener multi = new MultiListener(); + manager.addExpressionListener(single); + manager.addExpressionListener(multi); + try { + IWatchExpression exp1 = manager.newWatchExpression("exp1"); + IWatchExpression exp2 = manager.newWatchExpression("exp2"); + IWatchExpression exp3 = manager.newWatchExpression("exp3"); + manager.addExpressions(new IExpression[]{exp1, exp2, exp3}); + IExpression[] expressions = manager.getExpressions(); + assertEquals("Wrong number of expressions", 3, expressions.length); + assertEquals(single.addedCallbacks, 3); + assertEquals(3, single.added.size()); + assertEquals(0, single.added.indexOf(exp1)); + assertEquals(1, single.added.indexOf(exp2)); + assertEquals(2, single.added.indexOf(exp3)); + assertEquals(0, single.removedCallbacks); + assertEquals(0, single.changedCallbacks); + assertEquals(1, multi.addedCallbacks); + assertEquals(0, multi.removedCallbacks); + assertEquals(0, multi.changedCallbacks); + assertEquals(0, indexOf(exp1, expressions)); + assertEquals(1, indexOf(exp2, expressions)); + assertEquals(2, indexOf(exp3, expressions)); + } finally { + manager.removeExpressionListener(single); + manager.removeExpressionListener(multi); + } + } + + /** + * Remove expressions and ensure proper call backs are received. + */ + public void testRemoveExpressions() { + IExpressionManager manager = getManager(); + SinlgeListener single = new SinlgeListener(); + MultiListener multi = new MultiListener(); + manager.addExpressionListener(single); + manager.addExpressionListener(multi); + try { + IWatchExpression exp1 = manager.newWatchExpression("exp1"); + IWatchExpression exp2 = manager.newWatchExpression("exp2"); + IWatchExpression exp3 = manager.newWatchExpression("exp3"); + manager.addExpressions(new IExpression[]{exp1, exp2, exp3}); + manager.removeExpressions(new IExpression[]{exp1, exp3}); + IExpression[] expressions = manager.getExpressions(); + assertEquals("Wrong number of expressions", 1, expressions.length); + assertEquals(single.addedCallbacks, 3); + assertEquals(3, single.added.size()); + assertEquals(0, single.added.indexOf(exp1)); + assertEquals(1, single.added.indexOf(exp2)); + assertEquals(2, single.added.indexOf(exp3)); + assertEquals(2, single.removedCallbacks); + assertEquals(0, single.removed.indexOf(exp1)); + assertEquals(1, single.removed.indexOf(exp3)); + assertEquals(0, single.changedCallbacks); + assertEquals(1, multi.addedCallbacks); + assertEquals(1, multi.removedCallbacks); + assertEquals(0, multi.removed.indexOf(exp1)); + assertEquals(1, multi.removed.indexOf(exp3)); + assertEquals(0, multi.changedCallbacks); + assertEquals(-1, indexOf(exp1, expressions)); + assertEquals(0, indexOf(exp2, expressions)); + assertEquals(-1, indexOf(exp3, expressions)); + } finally { + manager.removeExpressionListener(single); + manager.removeExpressionListener(multi); + } + } + + /** + * Change expressions and ensure proper call backs are received. + */ + public void testChangeExpressions() { + IExpressionManager manager = getManager(); + SinlgeListener single = new SinlgeListener(); + MultiListener multi = new MultiListener(); + manager.addExpressionListener(single); + manager.addExpressionListener(multi); + try { + IWatchExpression exp1 = manager.newWatchExpression("exp1"); + IWatchExpression exp2 = manager.newWatchExpression("exp2"); + IWatchExpression exp3 = manager.newWatchExpression("exp3"); + manager.addExpressions(new IExpression[]{exp1, exp2, exp3}); + IExpression[] expressions = manager.getExpressions(); + exp1.setEnabled(false); + exp2.setExpressionText("exp2changed"); + assertEquals("Wrong number of expressions", 3, expressions.length); + assertEquals(single.addedCallbacks, 3); + assertEquals(3, single.added.size()); + assertEquals(0, single.added.indexOf(exp1)); + assertEquals(1, single.added.indexOf(exp2)); + assertEquals(2, single.added.indexOf(exp3)); + assertEquals(0, single.removedCallbacks); + assertEquals(2, single.changedCallbacks); + assertEquals(0, single.changed.indexOf(exp1)); + assertEquals(1, single.changed.indexOf(exp2)); + assertEquals(1, multi.addedCallbacks); + assertEquals(0, multi.removedCallbacks); + assertEquals(2, multi.changedCallbacks); + assertEquals(0, multi.changed.indexOf(exp1)); + assertEquals(1, multi.changed.indexOf(exp2)); + assertEquals(0, indexOf(exp1, expressions)); + assertEquals(1, indexOf(exp2, expressions)); + assertEquals(2, indexOf(exp3, expressions)); + } finally { + manager.removeExpressionListener(single); + manager.removeExpressionListener(multi); + } + } + + /** + * Insert expressions and ensure proper call backs are received. + */ + public void testInsertBeforeExpressions() { + ExpressionManager manager = (ExpressionManager) getManager(); + SinlgeListener single = new SinlgeListener(); + MultiListener multi = new MultiListener(); + InsertMoveListener insert = new InsertMoveListener(); + try { + IWatchExpression exp1 = manager.newWatchExpression("exp1"); + IWatchExpression exp2 = manager.newWatchExpression("exp2"); + IWatchExpression exp3 = manager.newWatchExpression("exp3"); + IWatchExpression exp4 = manager.newWatchExpression("exp4"); + IWatchExpression exp5 = manager.newWatchExpression("exp5"); + manager.addExpressions(new IExpression[]{exp1, exp2, exp3}); + IExpression[] expressions = manager.getExpressions(); + assertEquals("Wrong number of expressions", 3, expressions.length); + assertEquals(0, indexOf(exp1, expressions)); + assertEquals(1, indexOf(exp2, expressions)); + assertEquals(2, indexOf(exp3, expressions)); + // add listeners + manager.addExpressionListener(single); + manager.addExpressionListener(multi); + manager.addExpressionListener(insert); + + manager.insertExpressions(new IExpression[] {exp4, exp5}, exp2, true); + + assertEquals(2, single.addedCallbacks); + assertEquals(2, single.added.size()); + assertEquals(0, single.removedCallbacks); + assertEquals(0, single.changedCallbacks); + assertEquals(1, multi.addedCallbacks); + assertEquals(2, multi.added.size()); + assertEquals(0, multi.removedCallbacks); + assertEquals(0, multi.changedCallbacks); + assertEquals(1, insert.insertedCallbacks); + assertEquals(1, insert.insertIndex); + assertEquals(0, insert.movedCallbacks); + assertEquals(2, insert.inserted.size()); + assertEquals(0, insert.inserted.indexOf(exp4)); + assertEquals(1, insert.inserted.indexOf(exp5)); + + expressions = manager.getExpressions(); + assertEquals("Wrong number of expressions", 5, expressions.length); + assertEquals(0, indexOf(exp1, expressions)); + assertEquals(1, indexOf(exp4, expressions)); + assertEquals(2, indexOf(exp5, expressions)); + assertEquals(3, indexOf(exp2, expressions)); + assertEquals(4, indexOf(exp3, expressions)); + + } finally { + manager.removeExpressionListener(single); + manager.removeExpressionListener(multi); + manager.removeExpressionListener(insert); + } + } + + /** + * Insert expressions and ensure proper call backs are received. + */ + public void testInsertAfterExpressions() { + ExpressionManager manager = (ExpressionManager) getManager(); + SinlgeListener single = new SinlgeListener(); + MultiListener multi = new MultiListener(); + InsertMoveListener insert = new InsertMoveListener(); + try { + IWatchExpression exp1 = manager.newWatchExpression("exp1"); + IWatchExpression exp2 = manager.newWatchExpression("exp2"); + IWatchExpression exp3 = manager.newWatchExpression("exp3"); + IWatchExpression exp4 = manager.newWatchExpression("exp4"); + IWatchExpression exp5 = manager.newWatchExpression("exp5"); + manager.addExpressions(new IExpression[]{exp1, exp2, exp3}); + IExpression[] expressions = manager.getExpressions(); + assertEquals("Wrong number of expressions", 3, expressions.length); + assertEquals(0, indexOf(exp1, expressions)); + assertEquals(1, indexOf(exp2, expressions)); + assertEquals(2, indexOf(exp3, expressions)); + // add listeners + manager.addExpressionListener(single); + manager.addExpressionListener(multi); + manager.addExpressionListener(insert); + + manager.insertExpressions(new IExpression[] {exp4, exp5}, exp2, false); + + assertEquals(2, single.addedCallbacks); + assertEquals(2, single.added.size()); + assertEquals(0, single.removedCallbacks); + assertEquals(0, single.changedCallbacks); + assertEquals(1, multi.addedCallbacks); + assertEquals(2, multi.added.size()); + assertEquals(0, multi.removedCallbacks); + assertEquals(0, multi.changedCallbacks); + assertEquals(1, insert.insertedCallbacks); + assertEquals(2, insert.insertIndex); + assertEquals(0, insert.movedCallbacks); + assertEquals(2, insert.inserted.size()); + assertEquals(0, insert.inserted.indexOf(exp4)); + assertEquals(1, insert.inserted.indexOf(exp5)); + + expressions = manager.getExpressions(); + assertEquals("Wrong number of expressions", 5, expressions.length); + assertEquals(0, indexOf(exp1, expressions)); + assertEquals(1, indexOf(exp2, expressions)); + assertEquals(2, indexOf(exp4, expressions)); + assertEquals(3, indexOf(exp5, expressions)); + assertEquals(4, indexOf(exp3, expressions)); + + } finally { + manager.removeExpressionListener(single); + manager.removeExpressionListener(multi); + manager.removeExpressionListener(insert); + } + } + + /** + * Move expressions and ensure proper call backs are received. + */ + public void testMoveBeforeExpressions() { + ExpressionManager manager = (ExpressionManager) getManager(); + SinlgeListener single = new SinlgeListener(); + MultiListener multi = new MultiListener(); + InsertMoveListener insert = new InsertMoveListener(); + try { + IWatchExpression exp1 = manager.newWatchExpression("exp1"); + IWatchExpression exp2 = manager.newWatchExpression("exp2"); + IWatchExpression exp3 = manager.newWatchExpression("exp3"); + IWatchExpression exp4 = manager.newWatchExpression("exp4"); + IWatchExpression exp5 = manager.newWatchExpression("exp5"); + manager.addExpressions(new IExpression[]{exp1, exp2, exp3, exp4, exp5}); + // add listeners + manager.addExpressionListener(single); + manager.addExpressionListener(multi); + manager.addExpressionListener(insert); + + manager.moveExpressions(new IExpression[]{exp1,exp2}, exp5, true); + + assertEquals(0, single.addedCallbacks); + assertEquals(0, single.removedCallbacks); + assertEquals(0, single.changedCallbacks); + assertEquals(0, multi.addedCallbacks); + assertEquals(0, multi.removedCallbacks); + assertEquals(0, multi.changedCallbacks); + assertEquals(0, insert.insertedCallbacks); + assertEquals(1, insert.movedCallbacks); + assertEquals(2, insert.moved.size()); + assertEquals(0, insert.moved.indexOf(exp1)); + assertEquals(1, insert.moved.indexOf(exp2)); + assertEquals(2, insert.insertIndex); + + IExpression[] expressions = manager.getExpressions(); + assertEquals("Wrong number of expressions", 5, expressions.length); + assertEquals(0, indexOf(exp3, expressions)); + assertEquals(1, indexOf(exp4, expressions)); + assertEquals(2, indexOf(exp1, expressions)); + assertEquals(3, indexOf(exp2, expressions)); + assertEquals(4, indexOf(exp5, expressions)); + + } finally { + manager.removeExpressionListener(single); + manager.removeExpressionListener(multi); + manager.removeExpressionListener(insert); + } + } + + /** + * Move expressions and ensure proper call backs are received. + */ + public void testMoveAfterExpressions() { + ExpressionManager manager = (ExpressionManager) getManager(); + SinlgeListener single = new SinlgeListener(); + MultiListener multi = new MultiListener(); + InsertMoveListener insert = new InsertMoveListener(); + try { + IWatchExpression exp1 = manager.newWatchExpression("exp1"); + IWatchExpression exp2 = manager.newWatchExpression("exp2"); + IWatchExpression exp3 = manager.newWatchExpression("exp3"); + IWatchExpression exp4 = manager.newWatchExpression("exp4"); + IWatchExpression exp5 = manager.newWatchExpression("exp5"); + manager.addExpressions(new IExpression[]{exp1, exp2, exp3, exp4, exp5}); + // add listeners + manager.addExpressionListener(single); + manager.addExpressionListener(multi); + manager.addExpressionListener(insert); + + manager.moveExpressions(new IExpression[]{exp1,exp2}, exp3, false); + + assertEquals(0, single.addedCallbacks); + assertEquals(0, single.removedCallbacks); + assertEquals(0, single.changedCallbacks); + assertEquals(0, multi.addedCallbacks); + assertEquals(0, multi.removedCallbacks); + assertEquals(0, multi.changedCallbacks); + assertEquals(0, insert.insertedCallbacks); + assertEquals(1, insert.movedCallbacks); + assertEquals(2, insert.moved.size()); + assertEquals(0, insert.moved.indexOf(exp1)); + assertEquals(1, insert.moved.indexOf(exp2)); + assertEquals(1, insert.insertIndex); + + IExpression[] expressions = manager.getExpressions(); + assertEquals("Wrong number of expressions", 5, expressions.length); + assertEquals(0, indexOf(exp3, expressions)); + assertEquals(1, indexOf(exp1, expressions)); + assertEquals(2, indexOf(exp2, expressions)); + assertEquals(3, indexOf(exp4, expressions)); + assertEquals(4, indexOf(exp5, expressions)); + + } finally { + manager.removeExpressionListener(single); + manager.removeExpressionListener(multi); + manager.removeExpressionListener(insert); + } + } + + /** + * Test persist and restore of expressions + */ + public void testPersistExpressions() { + ExpressionManager manager = (ExpressionManager) getManager(); + IWatchExpression exp1 = manager.newWatchExpression("exp1"); + IWatchExpression exp2 = manager.newWatchExpression("exp2"); + IWatchExpression exp3 = manager.newWatchExpression("exp3"); + IWatchExpression exp4 = manager.newWatchExpression("exp4"); + IWatchExpression exp5 = manager.newWatchExpression("exp5"); + manager.addExpressions(new IExpression[]{exp1, exp2, exp3, exp4, exp5}); + manager.storeWatchExpressions(); + + // create a new manager that will restore the expressions + ExpressionManager manager2 = new ExpressionManager(); + IExpression[] expressions = manager2.getExpressions(); + assertEquals("Wrong number of expressions", 5, expressions.length); + assertEquals("exp1", expressions[0].getExpressionText()); + assertEquals("exp2", expressions[1].getExpressionText()); + assertEquals("exp3", expressions[2].getExpressionText()); + assertEquals("exp4", expressions[3].getExpressionText()); + assertEquals("exp5", expressions[4].getExpressionText()); + } + + /** + * Tests concurrent access to expressions. + * + * @throws InterruptedException + */ + public void testConcurrentAccess() throws InterruptedException { + final boolean[] done = new boolean[]{false}; + final Exception[] ex = new Exception[]{null}; + Runnable add = new Runnable() { + public void run() { + try { + for (int i = 0; i < 1000; i++) { + getManager().addExpression(getManager().newWatchExpression(Integer.toHexString(i))); + } + done[0] = true; + } catch (Exception e) { + ex[0] = e; + } + } + }; + Runnable remove = new Runnable() { + public void run() { + try { + do { + getManager().removeExpressions(getManager().getExpressions()); + } while (!done[0] || getManager().getExpressions().length > 0); + } catch (Exception e) { + ex[0] = e; + } + } + }; + Thread t1 = new Thread(add); + Thread t2 = new Thread(remove); + t1.start(); + t2.start(); + t1.join(); + t2.join(); + assertEquals(0, getManager().getExpressions().length); + assertNull(ex[0]); + } + +} -- cgit v1.2.3