[177324] IterateExpression should attempt to adapt to Collection
diff --git a/bundles/org.eclipse.core.expressions/src/org/eclipse/core/expressions/ICountable.java b/bundles/org.eclipse.core.expressions/src/org/eclipse/core/expressions/ICountable.java
new file mode 100644
index 0000000..cfcd519
--- /dev/null
+++ b/bundles/org.eclipse.core.expressions/src/org/eclipse/core/expressions/ICountable.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2007 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.core.expressions;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IAdapterManager;
+
+/**
+ * Objects that are adaptable to <code>ICountable</code> can be used
+ * as the default variable in a count expression.
+ *
+ * @see IAdaptable
+ * @see IAdapterManager
+ *
+ * @since 3.3
+ */
+public interface ICountable {
+
+ /**
+ * Returns the number of elements.
+ *
+ * @return the number of elements
+ */
+ public int count();
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.expressions/src/org/eclipse/core/expressions/IIterable.java b/bundles/org.eclipse.core.expressions/src/org/eclipse/core/expressions/IIterable.java
new file mode 100644
index 0000000..0a75649
--- /dev/null
+++ b/bundles/org.eclipse.core.expressions/src/org/eclipse/core/expressions/IIterable.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2007 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.core.expressions;
+
+import java.util.Iterator;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IAdapterManager;
+
+/**
+ * Objects that are adaptable to <code>IIterable</code> can be used
+ * as the default variable in an iterate expression.
+ *
+ * @see IAdaptable
+ * @see IAdapterManager
+ *
+ * @since 3.3
+ */
+public interface IIterable {
+
+ /**
+ * Returns an iterator to iterate over the elements.
+ *
+ * @return an iterator
+ */
+ public Iterator iterator();
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.expressions/src/org/eclipse/core/internal/expressions/CountExpression.java b/bundles/org.eclipse.core.expressions/src/org/eclipse/core/internal/expressions/CountExpression.java
index 9c1903d..048533b 100644
--- a/bundles/org.eclipse.core.expressions/src/org/eclipse/core/internal/expressions/CountExpression.java
+++ b/bundles/org.eclipse.core.expressions/src/org/eclipse/core/internal/expressions/CountExpression.java
@@ -20,6 +20,7 @@
import org.eclipse.core.expressions.EvaluationResult;
import org.eclipse.core.expressions.Expression;
import org.eclipse.core.expressions.ExpressionInfo;
+import org.eclipse.core.expressions.ICountable;
import org.eclipse.core.expressions.IEvaluationContext;
@@ -77,9 +78,15 @@
public EvaluationResult evaluate(IEvaluationContext context) throws CoreException {
Object var= context.getDefaultVariable();
- Expressions.checkCollection(var, this);
- Collection collection= (Collection)var;
- int size= collection.size();
+ int size;
+ if (var instanceof Collection) {
+ size= ((Collection)var).size();
+ } else {
+ ICountable countable= Expressions.getAsICountable(var, this);
+ if (countable == null)
+ return EvaluationResult.NOT_LOADED;
+ size= countable.count();
+ }
switch (fMode) {
case UNKNOWN:
return EvaluationResult.FALSE;
diff --git a/bundles/org.eclipse.core.expressions/src/org/eclipse/core/internal/expressions/ExpressionMessages.java b/bundles/org.eclipse.core.expressions/src/org/eclipse/core/internal/expressions/ExpressionMessages.java
index 2526357..208d5e8 100644
--- a/bundles/org.eclipse.core.expressions/src/org/eclipse/core/internal/expressions/ExpressionMessages.java
+++ b/bundles/org.eclipse.core.expressions/src/org/eclipse/core/internal/expressions/ExpressionMessages.java
@@ -24,6 +24,10 @@
public static String Expression_attribute_invalid_value;
public static String Expression_variable_not_a_collection;
public static String Expression_variable_not_a_list;
+
+ public static String Expression_variable_not_iterable;
+ public static String Expression_variable_not_countable;
+
public static String Expression_unknown_element;
public static String Missing_Expression;
public static String Expression_string_not_correctly_escaped;
diff --git a/bundles/org.eclipse.core.expressions/src/org/eclipse/core/internal/expressions/ExpressionMessages.properties b/bundles/org.eclipse.core.expressions/src/org/eclipse/core/internal/expressions/ExpressionMessages.properties
index 8e36280..858ad56 100644
--- a/bundles/org.eclipse.core.expressions/src/org/eclipse/core/internal/expressions/ExpressionMessages.properties
+++ b/bundles/org.eclipse.core.expressions/src/org/eclipse/core/internal/expressions/ExpressionMessages.properties
@@ -15,6 +15,9 @@
Expression_variable_not_a_collection= The default variable is not of type java.util.Collection. Failed expression: \n{0}
Expression_variable_not_a_list= The default variable is not of type java.util.List. Failed expression: \n{0}
+Expression_variable_not_iterable= The default variable is not iterable. Failed expression: \n{0}
+Expression_variable_not_countable= The default variable is not countable. Failed expression: \n{0}
+
Expression_unknown_element= Unknown expression element {0}
Missing_Expression= Unable to locate expression definition {0}
diff --git a/bundles/org.eclipse.core.expressions/src/org/eclipse/core/internal/expressions/Expressions.java b/bundles/org.eclipse.core.expressions/src/org/eclipse/core/internal/expressions/Expressions.java
index 77c85d4..68cd97b 100644
--- a/bundles/org.eclipse.core.expressions/src/org/eclipse/core/internal/expressions/Expressions.java
+++ b/bundles/org.eclipse.core.expressions/src/org/eclipse/core/internal/expressions/Expressions.java
@@ -17,10 +17,13 @@
import org.w3c.dom.Element;
import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdapterManager;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.expressions.Expression;
+import org.eclipse.core.expressions.ICountable;
+import org.eclipse.core.expressions.IIterable;
public class Expressions {
@@ -91,6 +94,64 @@
Messages.format(ExpressionMessages.Expression_variable_not_a_list, expression.toString())));
}
+ /**
+ * Converts the given variable into an <code>IIterable</code>. If a corresponding adapter can't be found an
+ * exception is thrown. If the corresponding adapter isn't loaded yet, <code>null</code> is returned.
+ *
+ * @param var the variable to turn into an <code>IIterable</code>
+ * @param expression the expression referring to the variable
+ *
+ * @return the <code>IIterable</code> or <code>null<code> if a corresponding adapter isn't loaded yet
+ *
+ * @throws CoreException if the var can't be adapted to an <code>IIterable</code>
+ */
+ public static IIterable getAsIIterable(Object var, Expression expression) throws CoreException {
+ if (var instanceof IIterable) {
+ return (IIterable)var;
+ } else {
+ IAdapterManager manager= Platform.getAdapterManager();
+ IIterable result= (IIterable)manager.getAdapter(var, IIterable.class);
+ if (result != null)
+ return result;
+
+ if (manager.queryAdapter(var, IIterable.class.getName()) == IAdapterManager.NOT_LOADED)
+ return null;
+
+ throw new CoreException(new ExpressionStatus(
+ ExpressionStatus.VARIABLE_IS_NOT_A_COLLECTION,
+ Messages.format(ExpressionMessages.Expression_variable_not_iterable, expression.toString())));
+ }
+ }
+
+ /**
+ * Converts the given variable into an <code>ICountable</code>. If a corresponding adapter can't be found an
+ * exception is thrown. If the corresponding adapter isn't loaded yet, <code>null</code> is returned.
+ *
+ * @param var the variable to turn into an <code>ICountable</code>
+ * @param expression the expression referring to the variable
+ *
+ * @return the <code>ICountable</code> or <code>null<code> if a corresponding adapter isn't loaded yet
+ *
+ * @throws CoreException if the var can't be adapted to an <code>ICountable</code>
+ */
+ public static ICountable getAsICountable(Object var, Expression expression) throws CoreException {
+ if (var instanceof ICountable) {
+ return (ICountable)var;
+ } else {
+ IAdapterManager manager= Platform.getAdapterManager();
+ ICountable result= (ICountable)manager.getAdapter(var, ICountable.class);
+ if (result != null)
+ return result;
+
+ if (manager.queryAdapter(var, ICountable.class.getName()) == IAdapterManager.NOT_LOADED)
+ return null;
+
+ throw new CoreException(new ExpressionStatus(
+ ExpressionStatus.VARIABLE_IS_NOT_A_COLLECTION,
+ Messages.format(ExpressionMessages.Expression_variable_not_countable, expression.toString())));
+ }
+ }
+
public static boolean getOptionalBooleanAttribute(IConfigurationElement element, String attributeName) {
String value= element.getAttribute(attributeName);
if (value == null)
diff --git a/bundles/org.eclipse.core.expressions/src/org/eclipse/core/internal/expressions/IterateExpression.java b/bundles/org.eclipse.core.expressions/src/org/eclipse/core/internal/expressions/IterateExpression.java
index a5893ba..468e90a 100644
--- a/bundles/org.eclipse.core.expressions/src/org/eclipse/core/internal/expressions/IterateExpression.java
+++ b/bundles/org.eclipse.core.expressions/src/org/eclipse/core/internal/expressions/IterateExpression.java
@@ -23,6 +23,7 @@
import org.eclipse.core.expressions.EvaluationResult;
import org.eclipse.core.expressions.ExpressionInfo;
import org.eclipse.core.expressions.IEvaluationContext;
+import org.eclipse.core.expressions.IIterable;
public class IterateExpression extends CompositeExpression {
@@ -135,38 +136,71 @@
*/
public EvaluationResult evaluate(IEvaluationContext context) throws CoreException {
Object var= context.getDefaultVariable();
- Expressions.checkCollection(var, this);
- Collection col= (Collection)var;
- switch (col.size()) {
- case 0:
+ if (var instanceof Collection) {
+ Collection col= (Collection)var;
+ switch (col.size()) {
+ case 0:
+ if (fEmptyResult == null) {
+ return fOperator == AND ? EvaluationResult.TRUE : EvaluationResult.FALSE;
+ } else {
+ return fEmptyResult.booleanValue() ? EvaluationResult.TRUE : EvaluationResult.FALSE;
+ }
+ case 1:
+ if (col instanceof List)
+ return evaluateAnd(new DefaultVariable(context, ((List)col).get(0)));
+ // fall through
+ default:
+ IteratePool iter= new IteratePool(context, col.iterator());
+ EvaluationResult result= fOperator == AND ? EvaluationResult.TRUE : EvaluationResult.FALSE;
+ while (iter.hasNext()) {
+ iter.next();
+ switch(fOperator) {
+ case OR:
+ result= result.or(evaluateAnd(iter));
+ if (result == EvaluationResult.TRUE)
+ return result;
+ break;
+ case AND:
+ result= result.and(evaluateAnd(iter));
+ if (result != EvaluationResult.TRUE)
+ return result;
+ break;
+ }
+ }
+ return result;
+ }
+ } else {
+ IIterable iterable= Expressions.getAsIIterable(var, this);
+ if (iterable == null)
+ return EvaluationResult.NOT_LOADED;
+ int count= 0;
+ IteratePool iter= new IteratePool(context, iterable.iterator());
+ EvaluationResult result= fOperator == AND ? EvaluationResult.TRUE : EvaluationResult.FALSE;
+ while (iter.hasNext()) {
+ iter.next();
+ count++;
+ switch(fOperator) {
+ case OR:
+ result= result.or(evaluateAnd(iter));
+ if (result == EvaluationResult.TRUE)
+ return result;
+ break;
+ case AND:
+ result= result.and(evaluateAnd(iter));
+ if (result != EvaluationResult.TRUE)
+ return result;
+ break;
+ }
+ }
+ if (count > 0) {
+ return result;
+ } else {
if (fEmptyResult == null) {
return fOperator == AND ? EvaluationResult.TRUE : EvaluationResult.FALSE;
} else {
return fEmptyResult.booleanValue() ? EvaluationResult.TRUE : EvaluationResult.FALSE;
}
- case 1:
- if (col instanceof List)
- return evaluateAnd(new DefaultVariable(context, ((List)col).get(0)));
- // fall through
- default:
- IteratePool iter= new IteratePool(context, col.iterator());
- EvaluationResult result= fOperator == AND ? EvaluationResult.TRUE : EvaluationResult.FALSE;
- while (iter.hasNext()) {
- iter.next();
- switch(fOperator) {
- case OR:
- result= result.or(evaluateAnd(iter));
- if (result == EvaluationResult.TRUE)
- return result;
- break;
- case AND:
- result= result.and(evaluateAnd(iter));
- if (result != EvaluationResult.TRUE)
- return result;
- break;
- }
- }
- return result;
+ }
}
}
@@ -190,4 +224,4 @@
return HASH_INITIAL * HASH_FACTOR + hashCode(fExpressions)
* HASH_FACTOR + fOperator;
}
-}
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.core.expressions.tests/plugin.xml b/tests/org.eclipse.core.expressions.tests/plugin.xml
index 474e7ee..adfe0bb 100644
--- a/tests/org.eclipse.core.expressions.tests/plugin.xml
+++ b/tests/org.eclipse.core.expressions.tests/plugin.xml
@@ -64,6 +64,12 @@
adaptableType="org.eclipse.core.internal.expressions.tests.Adaptee">
<adapter type="org.eclipse.core.internal.expressions.tests.Adapter"/>
</factory>
+ <factory
+ adaptableType="org.eclipse.core.internal.expressions.tests.ExpressionTests$CollectionWrapper"
+ class="org.eclipse.core.internal.expressions.tests.CollectionAdapterFactory">
+ <adapter type="org.eclipse.core.expressions.IIterable"/>
+ <adapter type="org.eclipse.core.expressions.ICountable"/>
+ </factory>
</extension>
<extension-point id="testParticipants" name="%testParticipant" schema="schema/testParticipants.exsd"/>
diff --git a/tests/org.eclipse.core.expressions.tests/src/org/eclipse/core/internal/expressions/tests/CollectionAdapterFactory.java b/tests/org.eclipse.core.expressions.tests/src/org/eclipse/core/internal/expressions/tests/CollectionAdapterFactory.java
new file mode 100644
index 0000000..87df052
--- /dev/null
+++ b/tests/org.eclipse.core.expressions.tests/src/org/eclipse/core/internal/expressions/tests/CollectionAdapterFactory.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2007 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.core.internal.expressions.tests;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.eclipse.core.runtime.IAdapterFactory;
+
+import org.eclipse.core.expressions.ICountable;
+import org.eclipse.core.expressions.IIterable;
+
+
+public class CollectionAdapterFactory implements IAdapterFactory {
+
+ public Object getAdapter(final Object adaptableObject, Class adapterType) {
+ if (adapterType.equals(IIterable.class) && adaptableObject instanceof ExpressionTests.CollectionWrapper) {
+ return new IIterable() {
+ public Iterator iterator() {
+ return ((ExpressionTests.CollectionWrapper)adaptableObject).collection.iterator();
+ }
+ };
+ }
+ if (adapterType.equals(ICountable.class) && adaptableObject instanceof ExpressionTests.CollectionWrapper) {
+ return new ICountable() {
+ public int count() {
+ return ((ExpressionTests.CollectionWrapper)adaptableObject).collection.size();
+ }
+ };
+ }
+ return null;
+ }
+
+ public Class[] getAdapterList() {
+ return new Class[] {Collection.class};
+ }
+
+}
diff --git a/tests/org.eclipse.core.expressions.tests/src/org/eclipse/core/internal/expressions/tests/ExpressionTests.java b/tests/org.eclipse.core.expressions.tests/src/org/eclipse/core/internal/expressions/tests/ExpressionTests.java
index 77df29a..41c42d9 100644
--- a/tests/org.eclipse.core.expressions.tests/src/org/eclipse/core/internal/expressions/tests/ExpressionTests.java
+++ b/tests/org.eclipse.core.expressions.tests/src/org/eclipse/core/internal/expressions/tests/ExpressionTests.java
@@ -12,6 +12,7 @@
import java.net.URL;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -39,6 +40,7 @@
import org.eclipse.core.internal.expressions.CountExpression;
import org.eclipse.core.internal.expressions.EnablementExpression;
import org.eclipse.core.internal.expressions.EqualsExpression;
+import org.eclipse.core.internal.expressions.ExpressionStatus;
import org.eclipse.core.internal.expressions.Expressions;
import org.eclipse.core.internal.expressions.InstanceofExpression;
import org.eclipse.core.internal.expressions.IterateExpression;
@@ -56,6 +58,10 @@
public class ExpressionTests extends TestCase {
+ public static class CollectionWrapper {
+ public Collection collection;
+ }
+
public static Test suite() {
return new TestSuite(ExpressionTests.class);
}
@@ -559,6 +565,42 @@
assertTrue(EvaluationResult.FALSE == exp.evaluate(context));
}
+ public void testCountExpressionNoneWithAdapterManager() throws Exception {
+ CountExpression exp= new CountExpression("!"); //$NON-NLS-1$
+
+ List list= new ArrayList();
+ CollectionWrapper wrapper= new CollectionWrapper();
+ wrapper.collection= list;
+
+ EvaluationContext context= new EvaluationContext(null, wrapper);
+ assertTrue(EvaluationResult.TRUE == exp.evaluate(context));
+
+ list.clear();
+ list.add("one"); //$NON-NLS-1$
+ context= new EvaluationContext(null, wrapper);
+ assertTrue(EvaluationResult.FALSE == exp.evaluate(context));
+
+ list.clear();
+ list.add("one"); //$NON-NLS-1$
+ list.add("two"); //$NON-NLS-1$
+ context= new EvaluationContext(null, wrapper);
+ assertTrue(EvaluationResult.FALSE == exp.evaluate(context));
+ }
+
+ public void testCountExpressionFailure() throws Exception {
+ CountExpression exp= new CountExpression("!"); //$NON-NLS-1$
+
+ EvaluationContext context= new EvaluationContext(null, new Object());
+ try {
+ EvaluationResult result= exp.evaluate(context);
+ fail("Count should've failed for non-Collection variable. Result = " +
+
+ result.toString());
+ } catch (CoreException e) {
+ assertEquals(ExpressionStatus.VARIABLE_IS_NOT_A_COLLECTION, e.getStatus().getCode());
+ }
+ }
+
public void testInstanceofTrue() throws Exception {
B b= new B();
EvaluationContext context= new EvaluationContext(null, b);
@@ -695,6 +737,76 @@
assertTrue(EvaluationResult.FALSE == exp.evaluate(context));
}
+ public void testIterateExpressionWithAdapterManager() throws Exception {
+ final List result= new ArrayList();
+ Expression myExpression= new Expression() {
+ public EvaluationResult evaluate(IEvaluationContext context) throws CoreException {
+ result.add(context.getDefaultVariable());
+ return EvaluationResult.FALSE;
+ }
+ };
+ IterateExpression exp= new IterateExpression("or"); //$NON-NLS-1$
+ exp.add(myExpression);
+ final List input= new ArrayList();
+ input.add("one"); //$NON-NLS-1$
+ input.add("two"); //$NON-NLS-1$
+ CollectionWrapper wrapper= new CollectionWrapper();
+ wrapper.collection= input;
+ EvaluationContext context= new EvaluationContext(null, wrapper);
+ assertTrue(EvaluationResult.FALSE == exp.evaluate(context));
+ assertTrue(result.equals(input));
+ }
+
+ public void testIterateExpressionWithAdapterManagerEmptyAnd() throws Exception {
+ IterateExpression exp= new IterateExpression("and"); //$NON-NLS-1$
+ final List input= new ArrayList();
+ CollectionWrapper wrapper= new CollectionWrapper();
+ wrapper.collection= input;
+ EvaluationContext context= new EvaluationContext(null, wrapper);
+ assertTrue(EvaluationResult.TRUE == exp.evaluate(context));
+ }
+
+ public void testIterateExpressionWithAdapterManagerEmptyOr() throws Exception {
+ IterateExpression exp= new IterateExpression("or"); //$NON-NLS-1$
+ final List input= new ArrayList();
+ CollectionWrapper wrapper= new CollectionWrapper();
+ wrapper.collection= input;
+ EvaluationContext context= new EvaluationContext(null, wrapper);
+ assertTrue(EvaluationResult.FALSE == exp.evaluate(context));
+ }
+
+ public void testIterateExpressionWithAdapterManagerIfEmptyFalse() throws Exception {
+ IterateExpression exp= new IterateExpression("or", "false"); //$NON-NLS-1$
+ final List input= new ArrayList();
+ CollectionWrapper wrapper= new CollectionWrapper();
+ wrapper.collection= input;
+ EvaluationContext context= new EvaluationContext(null, wrapper);
+ assertTrue(EvaluationResult.FALSE == exp.evaluate(context));
+ }
+
+ public void testIterateExpressionWithAdapterManagerIfEmptyTrue() throws Exception {
+ IterateExpression exp= new IterateExpression("or", "true"); //$NON-NLS-1$
+ final List input= new ArrayList();
+ CollectionWrapper wrapper= new CollectionWrapper();
+ wrapper.collection= input;
+ EvaluationContext context= new EvaluationContext(null, wrapper);
+ assertTrue(EvaluationResult.TRUE == exp.evaluate(context));
+ }
+
+ public void testIterateExpressionFailure() throws Exception {
+ IterateExpression exp= new IterateExpression((String)null);
+
+ EvaluationContext context= new EvaluationContext(null, new Object());
+ try {
+ EvaluationResult result= exp.evaluate(context);
+ fail("Count should've failed for non-Collection variable. Result = " +
+
+ result.toString());
+ } catch (CoreException e) {
+ assertEquals(ExpressionStatus.VARIABLE_IS_NOT_A_COLLECTION, e.getStatus().getCode());
+ }
+ }
+
public void testReadXMLExpression() throws Exception {
IExtensionRegistry registry= Platform.getExtensionRegistry();
IConfigurationElement[] ces= registry.getConfigurationElementsFor("org.eclipse.core.expressions.tests", "testParticipants"); //$NON-NLS-1$ //$NON-NLS-2$