content: /*******************************************************************************
   * Copyright (c) 2000, 2009 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.wst.jsdt.internal.corext.template.java;
  
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.HashMap;
  import java.util.List;
  import java.util.ListIterator;
  import java.util.Map;
  
  import org.eclipse.core.runtime.CoreException;
  import org.eclipse.core.runtime.IStatus;
  import org.eclipse.core.runtime.Status;
  import org.eclipse.wst.jsdt.core.CompletionProposal;
  import org.eclipse.wst.jsdt.core.CompletionRequestor;
  import org.eclipse.wst.jsdt.core.IJavaScriptProject;
  import org.eclipse.wst.jsdt.core.IJavaScriptUnit;
  import org.eclipse.wst.jsdt.core.IType;
  import org.eclipse.wst.jsdt.core.ITypeHierarchy;
  import org.eclipse.wst.jsdt.core.JavaScriptModelException;
  import org.eclipse.wst.jsdt.core.Signature;
  import org.eclipse.wst.jsdt.core.compiler.IProblem;
  import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin;
  
  /**
   * A completion requester to collect informations on local variables.
   * This class is used for guessing variable names like arrays, collections, etc.
   */
  final class CompilationUnitCompletion extends CompletionRequestor {
  
  	/**
  	 * Describes a local variable (including parameters) inside the method where
  	 * code completion was invoked. Special predicates exist to query whether
  	 * a variable can be iterated over. 
  	 */
  	public final class Variable {
  		private static final int UNKNOWN= 0, NONE= 0;
  		private static final int ARRAY= 1;
  		private static final int COLLECTION= 2;
  		private static final int ITERABLE= 4;
  		
  		/**
  		 * The name of the local variable.
  		 */
  		private final String name;
  		
  		/**
  		 * The signature of the local variable's type.
  		 */
  		private final String signature;
  		
  		/* lazily computed properties */
  		private int fType= UNKNOWN;
  		private int fChecked= NONE;
  		private String[] fMemberTypes;
  		
  		private Variable(String name, String signature) {
  			this.name= name;
  			this.signature= signature;
  		}
  
  		/**
  		 * Returns the name of the variable.
  		 * 
  		 * @return the name of the variable
  		 */
  		public String getName() {
  			return name;
  		}
  		
  		/**
  		 * Returns <code>true</code> if the type of the local variable is an
  		 * array type.
  		 * 
  		 * @return <code>true</code> if the receiver's type is an array,
  		 *         <code>false</code> if not
  		 */
  		public boolean isArray() {
  			
  			try
  			{
  				if (fType == UNKNOWN && (fChecked & ARRAY) == 0 && Signature.getTypeSignatureKind(signature) == Signature.ARRAY_TYPE_SIGNATURE)
  					fType= ARRAY;
  			} catch(IllegalArgumentException iae) {/* Ignore bad signature: assume not array */}
  			fChecked |= ARRAY;
  			return fType == ARRAY;
  		}
  
  		/**
  		 * Returns <code>true</code> if the receiver's type is a subclass of
  		 * <code>java.util.Collection</code>, <code>false</code> otherwise.
  		 * 
  		 * @return <code>true</code> if the receiver's type is a subclass of
  		 *         <code>java.util.Collection</code>, <code>false</code>
  		 *         otherwise
  		 */
  		public boolean isCollection() {
  			// Collection extends Iterable
  			if ((fType == UNKNOWN || fType == ITERABLE) && (fChecked & COLLECTION) == 0 && isSubtypeOf("java.util.Collection")) //$NON-NLS-1$
  				fType= COLLECTION;
  			fChecked |= COLLECTION;
  			return fType == COLLECTION;
  		}
  		
  		/**
  		 * Returns <code>true</code> if the receiver's type is a subclass of
  		 * <code>java.lang.Iterable</code>, <code>false</code> otherwise.
  		 * 
  		 * @return <code>true</code> if the receiver's type is a subclass of
  		 *         <code>java.lang.Iterable</code>, <code>false</code>
  		 *         otherwise
  		 */
  		public boolean isIterable() {
  			if (fType == UNKNOWN && (fChecked & ITERABLE) == 0 && isSubtypeOf("java.lang.Iterable")) //$NON-NLS-1$
  				fType= ITERABLE;
  			fChecked |= ITERABLE;
  			return fType == ITERABLE || fType == COLLECTION; // Collection extends Iterable
  		}
  
  		/**
  		 * Returns <code>true</code> if the receiver's type is an implementor
  		 * of <code>interfaceName</code>.
  		 * 
  		 * @param supertype the fully qualified name of the interface
  		 * @return <code>true</code> if the receiver's type implements the
  		 *         type named <code>interfaceName</code>
  		 */
  		private boolean isSubtypeOf(String supertype) {
  			String implementorName= SignatureUtil.stripSignatureToFQN(signature);
  			if (implementorName.length() == 0)
  				return false;
  			
  			boolean qualified= supertype.indexOf('.') != -1;
  			
  			// try cheap test first
  			if (implementorName.equals(supertype) || !qualified && Signature.getSimpleName(implementorName).equals(supertype))
  				return true;
  
  			if (fUnit == null)
  				return false;
  
  			IJavaScriptProject project= fUnit.getJavaScriptProject();
  
  			try {
  				IType sub= project.findType(implementorName);
  				if (sub == null)
  					return false;
  				
  				if (qualified) {
  					IType sup= project.findType(supertype);
  					if (sup == null)
  						return false;
  					ITypeHierarchy hierarchy= sub.newSupertypeHierarchy(null);
  					return hierarchy.contains(sup);
  				} else {
  					ITypeHierarchy hierarchy= sub.newSupertypeHierarchy(null);
  					IType[] allTypes= hierarchy.getAllClasses();
  					for (int i= 0; i < allTypes.length; i++) {
  						IType type= allTypes[i];
  						if (type.getElementName().equals(supertype))
  							return true;
  					}
  				}
  
  			} catch (JavaScriptModelException e) {
  				// ignore and return false
  			}			
  			
  			return false;
  		}
  		
  		private IType[] getSupertypes(String supertype) {
  			IType[] empty= new IType[0];
  			String implementorName= SignatureUtil.stripSignatureToFQN(signature);
  			if (implementorName.length() == 0)
  				return empty;
  			
  			boolean qualified= supertype.indexOf('.') != -1;
  
  			if (fUnit == null)
  				return empty;
  			
  			IJavaScriptProject project= fUnit.getJavaScriptProject();
  			
  			try {
  				IType sub= project.findType(implementorName);
  				if (sub == null)
  					return empty;
  				
  				if (qualified) {
  					IType sup= project.findType(supertype);
  					if (sup == null)
  						return empty;
  					return new IType[] {sup};
  				} else {
  					ITypeHierarchy hierarchy= sub.newSupertypeHierarchy(null);
  					IType[] allTypes= hierarchy.getAllClasses();
  					List matches= new ArrayList();
  					for (int i= 0; i < allTypes.length; i++) {
  						IType type= allTypes[i];
  						if (type.getElementName().equals(supertype))
  							matches.add(type);
  					}
  					return (IType[]) matches.toArray(new IType[matches.size()]);
  				}
  				
  			} catch (JavaScriptModelException e) {
  				// ignore and return false
  			}			
  			
  			return empty;
  		}
  
  		/**
  		 * Returns the signature of the member type.
  		 * 
  		 * @return the signature of the member type
  		 */
  		public String getMemberTypeSignature() {
  			return getMemberTypeSignatures()[0];
  		}
  		
  		/**
  		 * Returns the signatures of all member type bounds.
  		 * 
  		 * @return the signatures of all member type bounds
  		 */
  		public String[] getMemberTypeSignatures() {
  			if (isArray()) {
  				return new String[] {Signature.createArraySignature(Signature.getElementType(signature), Signature.getArrayCount(signature) - 1)};
  			} else if (fUnit != null && (isIterable() || isCollection())) {
  				if (fMemberTypes == null) {
  					try {
  						try {
  							TypeParameterResolver util= new TypeParameterResolver(this);
  							fMemberTypes= util.computeBinding("java.lang.Iterable", 0); //$NON-NLS-1$
  						} catch (JavaScriptModelException e) {
  							try {
  								TypeParameterResolver util= new TypeParameterResolver(this);
  								fMemberTypes= util.computeBinding("java.util.Collection", 0); //$NON-NLS-1$
  							} catch (JavaScriptModelException x) {
  								fMemberTypes= new String[0];
  							}
  						}
  					} catch (IndexOutOfBoundsException e) {
  						fMemberTypes= new String[0];
  					}
  				}
  				if (fMemberTypes.length > 0)
  					return fMemberTypes;
  			}
  			return new String[] {Signature.createTypeSignature("java.lang.Object", true)}; //$NON-NLS-1$
  		}
  		
  		/**
  		 * Returns the type names of all member type bounds, as they would be 
  		 * appear when referenced in the current compilation unit.
  		 * 
  		 * @return type names of all member type bounds
  		 */
  		public String[] getMemberTypeNames() {
  			String[] signatures= getMemberTypeSignatures();
  			String[] names= new String[signatures.length];
  			
  			for (int i= 0; i < signatures.length; i++) {
  				String sig= signatures[i];
  				String local= (String) fLocalTypes.get(Signature.getElementType(sig));
  				int dim= Signature.getArrayCount(sig);
  				if (local != null && dim > 0) {
  					StringBuffer array= new StringBuffer(local);
  					for (int j= 0; j < dim; j++)
  						array.append("[]"); //$NON-NLS-1$
  					local= array.toString();
  				}
  				if (local != null)
  					names[i]= local;
  				else
  					names[i]= Signature.getSimpleName(Signature.getSignatureSimpleName(sig));
  			}
  			return names;
  		}
  
  		/**
  		 * Returns the type arguments of the declared type of the variable. Returns
  		 * an empty array if it is not a parameterized type.
  		 * 
  		 * @param type the fully qualified type name of which to match a type argument  
  		 * @param index the index of the type parameter in the type
  		 * @return the type bounds for the specified type argument in this local variable
  		 * 
  		 */
  		public String[] getTypeArgumentBoundSignatures(String type, int index) {
  			List all= new ArrayList();
  			IType[] supertypes= getSupertypes(type);
  			if (fUnit != null) {
  				for (int i= 0; i < supertypes.length; i++) {
  					try {
  						TypeParameterResolver util= new TypeParameterResolver(this);
  						String[] result= util.computeBinding(supertypes[i].getFullyQualifiedName(), index);
  						all.addAll(Arrays.asList(result));
  					} catch (JavaScriptModelException e) {
  					} catch (IndexOutOfBoundsException e) {
  					}
  				}
  			}
  			if (all.isEmpty())
  				return new String[] {Signature.createTypeSignature("java.lang.Object", true)}; //$NON-NLS-1$
  			return (String[]) all.toArray(new String[all.size()]);
  		}
  
  		/*
  		 * @see java.lang.Object#toString()
  		 */
  		public String toString() {
  			String type;
  			switch (fType) {
  				case ITERABLE:
  					type= "ITERABLE"; //$NON-NLS-1$
  					break;
  				case COLLECTION:
  					type= "COLLECTION"; //$NON-NLS-1$
  					break;
  				case ARRAY:
  					type= "ARRAY"; //$NON-NLS-1$
  					break;
  				default:
  					type= "UNKNOWN"; //$NON-NLS-1$
  					break;
  			}
  			return "LocalVariable [name=\"" + name + "\" signature=\"" + signature + "\" type=\"" + type + "\" member=\"" + getMemberTypeSignature() + "\"]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
  		}
  	}
  	
  	/**
  	 * Given a java type, a resolver computes the bounds of type variables
  	 * declared in a super type, considering any type constraints along the
  	 * inheritance path.
  	 */
  	private final class TypeParameterResolver {
  		private static final String OBJECT_SIGNATURE= "Ljava.lang.Object;"; //$NON-NLS-1$
  		
  		private final ITypeHierarchy fHierarchy;
  		private final Variable fVariable;
  		private final IType fType;
  		private final List fBounds= new ArrayList();
  
  		/**
  		 * Creates a new type parameter resolver to compute the bindings of type
  		 * parameters for the declared type of <code>variable</code>. For any
  		 * super type of the type of <code>variable</code>, calling
  		 * {@link #computeBinding(IType, int) computeBinding} will find the type
  		 * bounds of type variables in the super type, considering any type
  		 * constraints along the inheritance path.
  		 * 
  		 * @param variable the local variable under investigation
  		 * @throws JavaScriptModelException if the type of <code>variable</code>
  		 *         cannot be found
  		 */
  		public TypeParameterResolver(Variable variable) throws JavaScriptModelException {
  			String typeName= SignatureUtil.stripSignatureToFQN(variable.signature);
  			IJavaScriptProject project= fUnit.getJavaScriptProject();
  			fType= project.findType(typeName);
  			fHierarchy= fType.newSupertypeHierarchy(null);
  			fVariable= variable;
  		}
  		
  		/**
  		 * Given a type parameter of <code>superType</code> at position
  		 * <code>index</code>, this method computes and returns the (lower)
  		 * type bound(s) of that parameter for an instance of <code>fType</code>.
  		 * <p>
  		 * <code>superType</code> must be a super type of <code>fType</code>,
  		 * and <code>superType</code> must have at least
  		 * <code>index + 1</code> type parameters.
  		 * </p>
  		 * 
  		 * @param superType the qualified type name of the super type to compute
  		 *        the type parameter binding for
  		 * @param index the index into the list of type parameters of
  		 *        <code>superType</code>
  		 * @throws JavaScriptModelException if any java model operation fails
  		 * @throws IndexOutOfBoundsException if the index is not valid
  		 */
  		public String[] computeBinding(String superType, int index) throws JavaScriptModelException, IndexOutOfBoundsException {
  			IJavaScriptProject project= fUnit.getJavaScriptProject();
  			IType type= project.findType(superType);
  			if (type == null)
  				throw new JavaScriptModelException(new CoreException(new Status(IStatus.ERROR, JavaScriptPlugin.getPluginId(), IStatus.OK, "No such type", null))); //$NON-NLS-1$
  			return computeBinding(type, index);
  		}
  
  		/**
  		 * Given a type parameter of <code>superType</code> at position
  		 * <code>index</code>, this method computes and returns the (lower)
  		 * type bound(s) of that parameter for an instance of <code>fType</code>.
  		 * <p>
  		 * <code>superType</code> must be a super type of <code>fType</code>,
  		 * and <code>superType</code> must have at least
  		 * <code>index + 1</code> type parameters.
  		 * </p>
  		 * 
  		 * @param superType the super type to compute the type parameter binding
  		 *        for
  		 * @param index the index into the list of type parameters of
  		 *        <code>superType</code>
  		 * @throws JavaScriptModelException if any java model operation fails
  		 * @throws IndexOutOfBoundsException if the index is not valid
  		 */
  		public String[] computeBinding(IType superType, int index) throws JavaScriptModelException, IndexOutOfBoundsException {
  			initBounds();
  			return (String[]) fBounds.toArray(new String[fBounds.size()]);
  		}
  		
  		/**
  		 * Clears the collected type bounds and initializes it with
  		 * <code>java.lang.Object</code>.
  		 */
  		private void initBounds() {
  			fBounds.clear();
  			fBounds.add(OBJECT_SIGNATURE); 
  		}
  
  		/**
  		 * Returns <code>true</code> if <code>subTypeSignature</code>
  		 * describes a type which is a true sub type of the type described by
  		 * <code>superTypeSignature</code>.
  		 * 
  		 * @param subTypeSignature the potential subtype's signature
  		 * @param superTypeSignature the potential supertype's signature
  		 * @return <code>true</code> if the inheritance relationship holds
  		 */
  		private boolean isTrueSubtypeOf(String subTypeSignature, String superTypeSignature) {
  			// try cheap test first
  			if (subTypeSignature.equals(superTypeSignature))
  				return true;
  			
  			if (SignatureUtil.isJavaLangObject(subTypeSignature))
  				return false; // Object has no super types
  			
  			if (Signature.getTypeSignatureKind(subTypeSignature) != Signature.BASE_TYPE_SIGNATURE && SignatureUtil.isJavaLangObject(superTypeSignature)) 
  				return true;
  			
  			IJavaScriptProject project= fUnit.getJavaScriptProject();
  			
  			try {
  				
  				if ((Signature.getTypeSignatureKind(subTypeSignature) & Signature.CLASS_TYPE_SIGNATURE) == 0)
  					return false;
  				IType subType= project.findType(SignatureUtil.stripSignatureToFQN(subTypeSignature));
  				if (subType == null)
  					return false;
  				
  				if ((Signature.getTypeSignatureKind(superTypeSignature) & Signature.CLASS_TYPE_SIGNATURE) == 0)
  					return false;
  				IType superType= project.findType(SignatureUtil.stripSignatureToFQN(superTypeSignature));
  				if (superType == null)
  					return false;
  				
  				ITypeHierarchy hierarchy= subType.newSupertypeHierarchy(null);
  				IType[] types= hierarchy.getAllSuperclasses(subType);
  				
  				for (int i= 0; i < types.length; i++)
  					if (types[i].equals(superType))
  						return true;
  			} catch (JavaScriptModelException e) {
  				// ignore and return false
  			}			
  			
  			return false;
  		}
  		
  		/**
  		 * Returns <code>true</code> if <code>signature</code> is a concrete type signature,
  		 * <code>false</code> if it is a type variable.
  		 * 
  		 * @param signature the signature to check
  		 * @param context the context inside which to resolve the type
  		 * @throws JavaScriptModelException if finding the type fails
  		 */
  		private boolean isConcreteType(String signature, IType context) throws JavaScriptModelException {
  			// try and resolve otherwise
  			if (context.isBinary()) {
  				return fUnit.getJavaScriptProject().findType(SignatureUtil.stripSignatureToFQN(signature)) != null;
  			} else {
  				return context.resolveType(SignatureUtil.stripSignatureToFQN(signature)) != null;
  			}
  		}
  	}
  	
  	private IJavaScriptUnit fUnit;
  
  	private List fLocalVariables= new ArrayList();
  	private List fFields= new ArrayList();
  	private Map fLocalTypes= new HashMap();
  
  	private boolean fError;
  
  	/**
  	 * Creates a compilation unit completion.
  	 * 
  	 * @param unit the compilation unit, may be <code>null</code>.
  	 */
  	CompilationUnitCompletion(IJavaScriptUnit unit) {
  		reset(unit);
  		setIgnored(CompletionProposal.ANONYMOUS_CLASS_DECLARATION, true);
  		setIgnored(CompletionProposal.KEYWORD, true);
  		setIgnored(CompletionProposal.LABEL_REF, true);
  		setIgnored(CompletionProposal.METHOD_DECLARATION, true);
  		setIgnored(CompletionProposal.METHOD_NAME_REFERENCE, true);
  		setIgnored(CompletionProposal.METHOD_REF, true);
  		setIgnored(CompletionProposal.PACKAGE_REF, true);
  		setIgnored(CompletionProposal.POTENTIAL_METHOD_DECLARATION, true);
  		setIgnored(CompletionProposal.VARIABLE_DECLARATION, true);
  		setIgnored(CompletionProposal.TYPE_REF, true);
  	}
  	
  	/**
  	 * Resets the completion requester.
  	 * 
  	 * @param unit the compilation unit, may be <code>null</code>.
  	 */
  	private void reset(IJavaScriptUnit unit) {
  		fUnit= unit;
  		fLocalVariables.clear();
  		fFields.clear();
  		fLocalTypes.clear();
  		
  		if (fUnit != null) {
  			try {
  				IType[] cuTypes= fUnit.getAllTypes();
  				for (int i= 0; i < cuTypes.length; i++) {
  					String fqn= cuTypes[i].getFullyQualifiedName();
  					String sig= Signature.createTypeSignature(fqn, true);
  					fLocalTypes.put(sig, cuTypes[i].getElementName());
  				}
  			} catch (JavaScriptModelException e) {
  				// ignore
  			}
  		}
  		fError= false;
  	}
  
  	/*
  	 * @see org.eclipse.wst.jsdt.core.CompletionRequestor#accept(org.eclipse.wst.jsdt.core.CompletionProposal)
  	 */
  	public void accept(CompletionProposal proposal) {
  		
  		String name= String.valueOf(proposal.getCompletion());
  		String signature = ( proposal.getSignature() != null ? String.valueOf( proposal.getSignature() ) : "" ); 
  		
  		switch (proposal.getKind()) {
  			
  			case CompletionProposal.LOCAL_VARIABLE_REF:
  				// collect local variables
  				fLocalVariables.add(new Variable(name, signature));
  				break;
  			case CompletionProposal.FIELD_REF:
  				// collect local variables
  				fFields.add(new Variable(name, signature));
  				break;
  				
  			default:
  				break;
  		}
  	}
  	
  	/*
  	 * @see org.eclipse.wst.jsdt.core.CompletionRequestor#completionFailure(org.eclipse.wst.jsdt.core.compiler.IProblem)
  	 */
  	public void completionFailure(IProblem problem) {
  		fError= true;
  	}
  
  	/**
  	 * Tests if the code completion process produced errors.
  	 * 
  	 * @return <code>true</code> if there are errors, <code>false</code>
  	 *         otherwise
  	 */
  	public boolean hasErrors() {
  		return fError;
  	}
  
  	/**
  	 * Returns all local variable names.
  	 * 
  	 * @return all local variable names
  	 */
  	public String[] getLocalVariableNames() {
  		String[] names= new String[fLocalVariables.size()];
  		int i= 0;
  		for (ListIterator iterator= fLocalVariables.listIterator(fLocalVariables.size()); iterator.hasPrevious();) {
  			Variable localVariable= (Variable) iterator.previous();
  			names[i++]= localVariable.getName();
  		}		
  		return names;
  	}	
  	
  	/**
  	 * Returns all field names.
  	 * 
  	 * @return all field names
  	 * 
  	 */
  	public String[] getFieldNames() {
  		String[] names= new String[fFields.size()];
  		int i= 0;
  		for (ListIterator iterator= fFields.listIterator(fFields.size()); iterator.hasPrevious();) {
  			Variable field= (Variable)iterator.previous();
  			names[i++]= field.getName();
  		}		
  		return names;
  	}	
  
  	/**
  	 * Returns all local arrays in the order that they appear.
  	 * 
  	 * @return all local arrays
  	 */
  	public Variable[] findLocalArrays() {
  		List arrays= new ArrayList();
  
  		for (ListIterator iterator= fLocalVariables.listIterator(fLocalVariables.size()); iterator.hasPrevious();) {
  			Variable localVariable= (Variable) iterator.previous();
  
  			if (localVariable.isArray())
  				arrays.add(localVariable);
  		}
  
  		return (Variable[]) arrays.toArray(new Variable[arrays.size()]);
  	}
  	
  	/**
  	 * Returns all local variables implementing or extending
  	 * <code>clazz</code> in the order that they appear.
  	 * 
  	 * @param clazz the fully qualified type name of the class to match
  	 * @return all local variables matching <code>clazz</code>
  	 */
  	public Variable[] findLocalVariables(String clazz) {
  		List matches= new ArrayList();
  		
  		for (ListIterator iterator= fLocalVariables.listIterator(fLocalVariables.size()); iterator.hasPrevious();) {
  			Variable localVariable= (Variable) iterator.previous();
  			
  			if (localVariable.isSubtypeOf(clazz))
  				matches.add(localVariable);
  		}
  		
  		return (Variable[]) matches.toArray(new Variable[matches.size()]);
  	}
  	
  	/**
  	 * Returns all local variables implementing or extending
  	 * <code>clazz</code> in the order that they appear.
  	 * 
  	 * @param clazz the fully qualified type name of the class to match
  	 * @return all local variables matching <code>clazz</code>
  	 */
  	public Variable[] findFieldVariables(String clazz) {
  		List matches= new ArrayList();
  		
  		for (ListIterator iterator= fFields.listIterator(fFields.size()); iterator.hasPrevious();) {
  			Variable localVariable= (Variable)iterator.previous();
  			
  			if (localVariable.isSubtypeOf(clazz))
  				matches.add(localVariable);
  		}
  		
  		return (Variable[]) matches.toArray(new Variable[matches.size()]);
  	}
  
  	/**
  	 * Returns all local variables implementing <code>java.lang.Iterable</code>
  	 * <em>and</em> all local arrays, in the order that they appear. That is, 
  	 * the returned variables can be used within the <code>foreach</code> 
  	 * language construct.
  	 * 
  	 * @return all local <code>Iterable</code>s and arrays
  	 */
  	public Variable[] findLocalIterables() {
  		List iterables= new ArrayList();
  
  		for (ListIterator iterator= fLocalVariables.listIterator(fLocalVariables.size()); iterator.hasPrevious();) {
  			Variable localVariable= (Variable) iterator.previous();
  
  			if (localVariable.isArray() || localVariable.isIterable())			
  				iterables.add(localVariable);
  		}
  
  		return (Variable[]) iterables.toArray(new Variable[iterables.size()]);
  	}
  
  }
  
  