[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$