/*******************************************************************************
 * Copyright (c) 2006 Sybase, Inc. 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:
 *     Sybase, Inc. - initial API and implementation
 *******************************************************************************/
package org.eclipse.jst.jsf.common.ui.internal.utils;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.ClasspathContainerInitializer;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IClasspathContainer;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IImportDeclaration;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.compiler.CharOperation;

/**
 * Original code is from JDT Utility methods for the Java Model.
 */
public final class JavaModelUtil {

	/**
	 * Finds a type by its qualified type name (dot separated).
	 * 
	 * @param jproject
	 *            The java project to search in
	 * @param fullyQualifiedName
	 *            The fully qualified name (type name with enclosing type names
	 *            and package (all separated by dots))
	 * @return The type found, or null if not existing
	 * @throws JavaModelException 
	 */
	public static IType findType(IJavaProject jproject,
			String fullyQualifiedName) throws JavaModelException {
		// workaround for bug 22883
		IType type = jproject.findType(fullyQualifiedName);
		if (type != null) {
			return type;
		}
		IPackageFragmentRoot[] roots = jproject.getPackageFragmentRoots();
		for (int i = 0; i < roots.length; i++) {
			IPackageFragmentRoot root = roots[i];
			type = findType(root, fullyQualifiedName);
			if (type != null && type.exists()) {
				return type;
			}
		}
		return null;
	}

	/**
	 * Returns <code>true</code> if the given package fragment root is
	 * referenced. This means it is own by a different project but is referenced
	 * by the root's parent. Returns <code>false</code> if the given root
	 * doesn't have an underlying resource.
	 * @param root 
	 * @return true if root is referenced
	 */
	public static boolean isReferenced(IPackageFragmentRoot root) {
		IResource resource = root.getResource();
		if (resource != null) {
			IProject jarProject = resource.getProject();
			IProject container = root.getJavaProject().getProject();
			return !container.equals(jarProject);
		}
		return false;
	}

	private static IType findType(IPackageFragmentRoot root,
			String fullyQualifiedName) throws JavaModelException {
		IJavaElement[] children = root.getChildren();
		for (int i = 0; i < children.length; i++) {
			IJavaElement element = children[i];
			if (element.getElementType() == IJavaElement.PACKAGE_FRAGMENT) {
				IPackageFragment pack = (IPackageFragment) element;
				if (!fullyQualifiedName.startsWith(pack.getElementName())) {
					continue;
				}
				IType type = findType(pack, fullyQualifiedName);
				if (type != null && type.exists()) {
					return type;
				}
			}
		}
		return null;
	}

	private static IType findType(IPackageFragment pack,
			String fullyQualifiedName) throws JavaModelException {
		ICompilationUnit[] cus = pack.getCompilationUnits();
		for (int i = 0; i < cus.length; i++) {
			ICompilationUnit unit = cus[i];
			IType type = findType(unit, fullyQualifiedName);
			if (type != null && type.exists()) {
				return type;
			}
		}
		return null;
	}

	private static IType findType(ICompilationUnit cu, String fullyQualifiedName)
			throws JavaModelException {
		IType[] types = cu.getAllTypes();
		for (int i = 0; i < types.length; i++) {
			IType type = types[i];
			if (getFullyQualifiedName(type).equals(fullyQualifiedName)) {
				return type;
			}
		}
		return null;
	}

	/**
	 * Finds a type by package and type name.
	 * 
	 * @param jproject
	 *            the java project to search in
	 * @param pack
	 *            The package name
	 * @param typeQualifiedName
	 *            the type qualified name (type name with enclosing type names
	 *            (separated by dots))
	 * @return the type found, or null if not existing
	 * @throws JavaModelException 
	 * @deprecated Use IJavaProject.findType(String, String) instead
	 */
	public static IType findType(IJavaProject jproject, String pack,
			String typeQualifiedName) throws JavaModelException {
		return jproject.findType(pack, typeQualifiedName);
	}

	/**
	 * Finds a type container by container name. The returned element will be of
	 * type <code>IType</code> or a <code>IPackageFragment</code>.
	 * <code>null</code> is returned if the type container could not be found.
	 * 
	 * @param jproject
	 *            The Java project defining the context to search
	 * @param typeContainerName
	 *            A dot separarted name of the type container
	 * @return the java element
	 * @throws JavaModelException 
	 * @see #getTypeContainerName(IType)
	 */
	public static IJavaElement findTypeContainer(IJavaProject jproject,
			String typeContainerName) throws JavaModelException {
		// try to find it as type
		IJavaElement result = jproject.findType(typeContainerName);
		if (result == null) {
			// find it as package
			IPath path = new Path(typeContainerName.replace('.', '/'));
			result = jproject.findElement(path);
			if (!(result instanceof IPackageFragment)) {
				result = null;
			}

		}
		return result;
	}

	/**
	 * Finds a type in a compilation unit. Typical usage is to find the
	 * corresponding type in a working copy.
	 * 
	 * @param cu
	 *            the compilation unit to search in
	 * @param typeQualifiedName
	 *            the type qualified name (type name with enclosing type names
	 *            (separated by dots))
	 * @return the type found, or null if not existing
	 * @throws JavaModelException 
	 */
	public static IType findTypeInCompilationUnit(ICompilationUnit cu,
			String typeQualifiedName) throws JavaModelException {
		IType[] types = cu.getAllTypes();
		for (int i = 0; i < types.length; i++) {
			String currName = getTypeQualifiedName(types[i]);
			if (typeQualifiedName.equals(currName)) {
				return types[i];
			}
		}
		return null;
	}

	/**
	 * Finds a a member in a compilation unit. Typical usage is to find the
	 * corresponding member in a working copy.
	 * 
	 * @param cu
	 *            the compilation unit (eg. working copy) to search in
	 * @param member
	 *            the member (eg. from the original)
	 * @return the member found, or null if not existing
	 */
	public static IMember findMemberInCompilationUnit(ICompilationUnit cu,
			IMember member) {
		IJavaElement[] elements = cu.findElements(member);
		if (elements != null && elements.length > 0) {
			return (IMember) elements[0];
		}
		return null;
	}

	/**
	 * Returns the element of the given compilation unit which is "equal" to the
	 * given element. Note that the given element usually has a parent different
	 * from the given compilation unit.
	 * 
	 * @param cu
	 *            the cu to search in
	 * @param element
	 *            the element to look for
	 * @return an element of the given cu "equal" to the given element
	 */
	public static IJavaElement findInCompilationUnit(ICompilationUnit cu,
			IJavaElement element) {
		IJavaElement[] elements = cu.findElements(element);
		if (elements != null && elements.length > 0) {
			return elements[0];
		}
		return null;
	}

	/**
	 * Returns the qualified type name of the given type using '.' as
	 * separators. This is a replace for IType.getTypeQualifiedName() which uses
	 * '$' as separators. As '$' is also a valid character in an id this is
	 * ambiguous. JavaCore PR: 1GCFUNT
	 * @param type 
	 * @return the type qualified name
	 */
	public static String getTypeQualifiedName(IType type) {
		return type.getTypeQualifiedName('.');
	}

	/**
	 * Returns the fully qualified name of the given type using '.' as
	 * separators. This is a replace for IType.getFullyQualifiedTypeName which
	 * uses '$' as separators. As '$' is also a valid character in an id this is
	 * ambiguous. JavaCore PR: 1GCFUNT
	 * @param type 
	 * @return the fully qualified name using . as the separator
	 */
	public static String getFullyQualifiedName(IType type) {
		return type.getFullyQualifiedName('.');
	}

	/**
	 * Returns the fully qualified name of a type's container. (package name or
	 * enclosing type name)
	 * @param type 
	 * @return the container name
	 */
	public static String getTypeContainerName(IType type) {
		IType outerType = type.getDeclaringType();
		if (outerType != null) {
			return outerType.getFullyQualifiedName('.');
		}
        return type.getPackageFragment().getElementName();
	}

	/**
	 * Concatenates two names. Uses a dot for separation. Both strings can be
	 * empty or <code>null</code>.
	 * @param name1 
	 * @param name2 
	 * @return name1 + name2
	 */
	public static String concatenateName(String name1, String name2) {
		StringBuffer buf = new StringBuffer();
		if (name1 != null && name1.length() > 0) {
			buf.append(name1);
		}
		if (name2 != null && name2.length() > 0) {
			if (buf.length() > 0) {
				buf.append('.');
			}
			buf.append(name2);
		}
		return buf.toString();
	}

	/**
	 * Concatenates two names. Uses a dot for separation. Both strings can be
	 * empty or <code>null</code>.
	 * @param name1 
	 * @param name2 
	 * @return name1 + name2
	 */
	public static String concatenateName(char[] name1, char[] name2) {
		StringBuffer buf = new StringBuffer();
		if (name1 != null && name1.length > 0) {
			buf.append(name1);
		}
		if (name2 != null && name2.length > 0) {
			if (buf.length() > 0) {
				buf.append('.');
			}
			buf.append(name2);
		}
		return buf.toString();
	}

	/**
	 * Evaluates if a member (possible from another package) is visible from
	 * elements in a package.
	 * 
	 * @param member
	 *            The member to test the visibility for
	 * @param pack
	 *            The package in focus
	 * @return true if visible
	 * @throws JavaModelException 
	 */
	public static boolean isVisible(IMember member, IPackageFragment pack)
			throws JavaModelException {

		int type = member.getElementType();
		if (type == IJavaElement.INITIALIZER
				|| (type == IJavaElement.METHOD && member.getElementName()
						.startsWith("<"))) {
			//$NON-NLS-1$
			return false;
		}

		int otherflags = member.getFlags();
		IType declaringType = member.getDeclaringType();
		if (Flags.isPublic(otherflags)
				|| (declaringType != null && declaringType.isInterface())) {
			return true;
		} else if (Flags.isPrivate(otherflags)) {
			return false;
		}

		IPackageFragment otherpack = (IPackageFragment) findParentOfKind(
				member, IJavaElement.PACKAGE_FRAGMENT);
		return (pack != null && otherpack != null && isSamePackage(pack,
				otherpack));
	}

	/**
	 * Evaluates if a member in the focus' element hierarchy is visible from
	 * elements in a package.
	 * 
	 * @param member
	 *            The member to test the visibility for
	 * @param pack
	 *            The package of the focus element focus
	 * @return true if is visible in hiearchy
	 * @throws JavaModelException 
	 */
	public static boolean isVisibleInHierarchy(IMember member,
			IPackageFragment pack) throws JavaModelException {
		int type = member.getElementType();
		if (type == IJavaElement.INITIALIZER
				|| (type == IJavaElement.METHOD && member.getElementName()
						.startsWith("<"))) {
			//$NON-NLS-1$
			return false;
		}

		int otherflags = member.getFlags();

		IType declaringType = member.getDeclaringType();
		if (Flags.isPublic(otherflags) || Flags.isProtected(otherflags)
				|| (declaringType != null && declaringType.isInterface())) {
			return true;
		} else if (Flags.isPrivate(otherflags)) {
			return false;
		}

		IPackageFragment otherpack = (IPackageFragment) findParentOfKind(
				member, IJavaElement.PACKAGE_FRAGMENT);
		return (pack != null && pack.equals(otherpack));
	}

	/**
	 * Returns the package fragment root of <code>IJavaElement</code>. If the
	 * given element is already a package fragment root, the element itself is
	 * returned.
	 * @param element 
	 * @return the package fragment root
	 */
	public static IPackageFragmentRoot getPackageFragmentRoot(
			IJavaElement element) {
		return (IPackageFragmentRoot) element
				.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
	}

	/**
	 * Returns the parent of the supplied java element that conforms to the
	 * given parent type or <code>null</code>, if such a parent doesn't exit.
	 * 
	 * @deprecated Use element.getParent().getAncestor(kind);
	 */
	private static IJavaElement findParentOfKind(IJavaElement element, int kind) {
		if (element != null && element.getParent() != null) {
			return element.getParent().getAncestor(kind);
		}
		return null;
	}

	/**
	 * Finds a method in a type. This searches for a method with the same name
	 * and signature. Parameter types are only compared by the simple name, no
	 * resolving for the fully qualified type name is done. Constructors are
	 * only compared by parameters, not the name.
	 * 
	 * @param name
	 *            The name of the method to find
	 * @param paramTypes
	 *            The type signatures of the parameters e.g.
	 *            <code>{"QString;","I"}</code>
	 * @param isConstructor
	 *            If the method is a constructor
	 * @param type 
	 * @return The first found method or <code>null</code>, if nothing found
	 * @throws JavaModelException 
	 */
	public static IMethod findMethod(String name, String[] paramTypes,
			boolean isConstructor, IType type) throws JavaModelException {
		return findMethod(name, paramTypes, isConstructor, type.getMethods());
	}

	/**
	 * Finds a method by name. This searches for a method with a name and
	 * signature. Parameter types are only compared by the simple name, no
	 * resolving for the fully qualified type name is done. Constructors are
	 * only compared by parameters, not the name.
	 * 
	 * @param name
	 *            The name of the method to find
	 * @param paramTypes
	 *            The type signatures of the parameters e.g.
	 *            <code>{"QString;","I"}</code>
	 * @param isConstructor
	 *            If the method is a constructor
	 * @param methods
	 *            The methods to search in
	 * @return The found method or <code>null</code>, if nothing found
	 * @throws JavaModelException 
	 */
	public static IMethod findMethod(String name, String[] paramTypes,
			boolean isConstructor, IMethod[] methods) throws JavaModelException {
		for (int i = methods.length - 1; i >= 0; i--) {
			if (isSameMethodSignature(name, paramTypes, isConstructor,
					methods[i])) {
				return methods[i];
			}
		}
		return null;
	}

	/**
	 * Finds a method declararion in a type's hierarchy. The search is top down,
	 * so this returns the first declaration of the method in the hierarchy.
	 * This searches for a method with a name and signature. Parameter types are
	 * only compared by the simple name, no resolving for the fully qualified
	 * type name is done. Constructors are only compared by parameters, not the
	 * name.
	 * @param hierarchy 
	 * 
	 * @param type
	 *            Searches in this type's supertypes.
	 * @param name
	 *            The name of the method to find
	 * @param paramTypes
	 *            The type signatures of the parameters e.g.
	 *            <code>{"QString;","I"}</code>
	 * @param isConstructor
	 *            If the method is a constructor
	 * @return The first method found or null, if nothing found
	 * @throws JavaModelException 
	 */
	public static IMethod findMethodDeclarationInHierarchy(
			ITypeHierarchy hierarchy, IType type, String name,
			String[] paramTypes, boolean isConstructor)
			throws JavaModelException {
		IType[] superTypes = hierarchy.getAllSupertypes(type);
		for (int i = superTypes.length - 1; i >= 0; i--) {
			IMethod first = findMethod(name, paramTypes, isConstructor,
					superTypes[i]);
			if (first != null && !Flags.isPrivate(first.getFlags())) {
				// the order getAllSupertypes does make assumptions of the order
				// of inner elements -> search recursivly
				IMethod res = findMethodDeclarationInHierarchy(hierarchy, first
						.getDeclaringType(), name, paramTypes, isConstructor);
				if (res != null) {
					return res;
				}
				return first;
			}
		}
		return null;
	}

	/**
	 * Finds a method implementation in a type's classhierarchy. The search is
	 * bottom-up, so this returns the nearest overridden method. Does not find
	 * methods in interfaces or abstract methods. This searches for a method
	 * with a name and signature. Parameter types are only compared by the
	 * simple name, no resolving for the fully qualified type name is done.
	 * Constructors are only compared by parameters, not the name.
	 * @param hierarchy 
	 * 
	 * @param type
	 *            Type to search the superclasses
	 * @param name
	 *            The name of the method to find
	 * @param paramTypes
	 *            The type signatures of the parameters e.g.
	 *            <code>{"QString;","I"}</code>
	 * @param isConstructor
	 *            If the method is a constructor
	 * @return The first method found or null, if nothing found
	 * @throws JavaModelException 
	 */
	public static IMethod findMethodImplementationInHierarchy(
			ITypeHierarchy hierarchy, IType type, String name,
			String[] paramTypes, boolean isConstructor)
			throws JavaModelException {
		IType[] superTypes = hierarchy.getAllSuperclasses(type);
		for (int i = 0; i < superTypes.length; i++) {
			IMethod found = findMethod(name, paramTypes, isConstructor,
					superTypes[i]);
			if (found != null) {
				if (Flags.isAbstract(found.getFlags())) {
					return null;
				}
				return found;
			}
		}
		return null;
	}

	private static IMethod findMethodInHierarchy(ITypeHierarchy hierarchy,
			IType type, String name, String[] paramTypes, boolean isConstructor)
			throws JavaModelException {
		IMethod method = findMethod(name, paramTypes, isConstructor, type);
		if (method != null) {
			return method;
		}
		IType superClass = hierarchy.getSuperclass(type);
		if (superClass != null) {
			IMethod res = findMethodInHierarchy(hierarchy, superClass, name,
					paramTypes, isConstructor);
			if (res != null) {
				return res;
			}
		}
		if (!isConstructor) {
			IType[] superInterfaces = hierarchy.getSuperInterfaces(type);
			for (int i = 0; i < superInterfaces.length; i++) {
				IMethod res = findMethodInHierarchy(hierarchy,
						superInterfaces[i], name, paramTypes, false);
				if (res != null) {
					return res;
				}
			}
		}
		return method;
	}

	/**
	 * Finds the method that is defines/declares the given method. The search is
	 * bottom-up, so this returns the nearest defining/declaring method.
	 * @param typeHierarchy 
	 * @param type 
	 * @param methodName 
	 * @param paramTypes 
	 * @param isConstructor 
	 * 
	 * @param testVisibility
	 *            If true the result is tested on visibility. Null is returned
	 *            if the method is not visible.
	 * @return the method or null
	 * @throws JavaModelException
	 */
	public static IMethod findMethodDefininition(ITypeHierarchy typeHierarchy,
			IType type, String methodName, String[] paramTypes,
			boolean isConstructor, boolean testVisibility)
			throws JavaModelException {
		IType superClass = typeHierarchy.getSuperclass(type);
		if (superClass != null) {
			IMethod res = findMethodInHierarchy(typeHierarchy, superClass,
					methodName, paramTypes, isConstructor);
			if (res != null && !Flags.isPrivate(res.getFlags())) {
				if (!testVisibility
						|| isVisibleInHierarchy(res, type.getPackageFragment())) {
					return res;
				}
			}
		}
		if (!isConstructor) {
			IType[] interfaces = typeHierarchy.getSuperInterfaces(type);
			for (int i = 0; i < interfaces.length; i++) {
				IMethod res = findMethodInHierarchy(typeHierarchy,
						interfaces[i], methodName, paramTypes, false);
				if (res != null) {
					return res; // methods from interfaces are always public and
					// therefore visible
				}
			}
		}
		return null;
	}

	/**
	 * Tests if a method equals to the given signature. Parameter types are only
	 * compared by the simple name, no resolving for the fully qualified type
	 * name is done. Constructors are only compared by parameters, not the name.
	 * 
	 * @param name
	 *            Name of the method
	 * @param paramTypes
	 *            The type signatures of the parameters e.g.
	 *            <code>{"QString;","I"}</code>
	 * @param isConstructor
	 *            Specifies if the method is a constructor
	 * @param curr 
	 * @return Returns <code>true</code> if the method has the given name and
	 *         parameter types and constructor state.
	 * @throws JavaModelException 
	 */
	public static boolean isSameMethodSignature(String name,
			String[] paramTypes, boolean isConstructor, IMethod curr)
			throws JavaModelException {
		if (isConstructor || name.equals(curr.getElementName())) {
			if (isConstructor == curr.isConstructor()) {
				String[] currParamTypes = curr.getParameterTypes();
				if (paramTypes.length == currParamTypes.length) {
					for (int i = 0; i < paramTypes.length; i++) {
						String t1 = Signature.getSimpleName(Signature
								.toString(paramTypes[i]));
						String t2 = Signature.getSimpleName(Signature
								.toString(currParamTypes[i]));
						if (!t1.equals(t2)) {
							return false;
						}
					}
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * Tests if two <code>IPackageFragment</code>s represent the same logical
	 * java package.
	 * @param pack1 
	 * @param pack2 
	 * 
	 * @return <code>true</code> if the package fragments' names are equal.
	 */
	public static boolean isSamePackage(IPackageFragment pack1,
			IPackageFragment pack2) {
		return pack1.getElementName().equals(pack2.getElementName());
	}

	/**
	 * Checks whether the given type has a valid main method or not.
	 * @param type 
	 * @return true if type has a main method
	 * @throws JavaModelException 
	 */
	public static boolean hasMainMethod(IType type) throws JavaModelException {
		IMethod[] methods = type.getMethods();
		for (int i = 0; i < methods.length; i++) {
			if (methods[i].isMainMethod()) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Checks if the field is boolean.
	 * @param field 
	 * @return true if the file is of primitive boolean type
	 * @throws JavaModelException 
	 */
	public static boolean isBoolean(IField field) throws JavaModelException {
		return field.getTypeSignature().equals(Signature.SIG_BOOLEAN);
	}

	/**
	 * Tests if the given element is on the class path of its containing
	 * project. Handles the case that the containing project isn't a Java
	 * project.
	 * @param element 
	 * @return true if element in on the class path?
	 */
	public static boolean isOnClasspath(IJavaElement element) {
		IJavaProject project = element.getJavaProject();
		if (!project.exists())
			return false;
		return project.isOnClasspath(element);
	}

	/**
	 * Resolves a type name in the context of the declaring type.
	 * 
	 * @param refTypeSig
	 *            the type name in signature notation (for example 'QVector')
	 *            this can also be an array type, but dimensions will be
	 *            ignored.
	 * @param declaringType
	 *            the context for resolving (type where the reference was made
	 *            in)
	 * @return returns the fully qualified type name or build-in-type name. if a
	 *         unresoved type couldn't be resolved null is returned
	 * @throws JavaModelException 
	 */
	public static String getResolvedTypeName(String refTypeSig,
			IType declaringType) throws JavaModelException {
		int arrayCount = Signature.getArrayCount(refTypeSig);
		char type = refTypeSig.charAt(arrayCount);
		if (type == Signature.C_UNRESOLVED) {
			int semi = refTypeSig
					.indexOf(Signature.C_SEMICOLON, arrayCount + 1);
			if (semi == -1) {
				throw new IllegalArgumentException();
			}
			String name = refTypeSig.substring(arrayCount + 1, semi);

			String[][] resolvedNames = declaringType.resolveType(name);
			if (resolvedNames != null && resolvedNames.length > 0) {
				return JavaModelUtil.concatenateName(resolvedNames[0][0],
						resolvedNames[0][1]);
			}
			return null;
		}
        return Signature.toString(refTypeSig.substring(arrayCount));
	}

	/**
	 * Returns if a CU can be edited.
	 * @param cu 
	 * @return true if cu is editable
	 */
	public static boolean isEditable(ICompilationUnit cu) {
		IResource resource = toOriginal(cu).getResource();
		return (resource.exists() && !resource.getResourceAttributes()
				.isReadOnly());
	}

	/**
	 * Finds a qualified import for a type name.
	 * @param cu 
	 * @param simpleName 
	 * @return the import declaration or null
	 * @throws JavaModelException 
	 */
	public static IImportDeclaration findImport(ICompilationUnit cu,
			String simpleName) throws JavaModelException {
		IImportDeclaration[] existing = cu.getImports();
		for (int i = 0; i < existing.length; i++) {
			String curr = existing[i].getElementName();
			if (curr.endsWith(simpleName)) {
				int dotPos = curr.length() - simpleName.length() - 1;
				if ((dotPos == -1)
						|| (dotPos > 0 && curr.charAt(dotPos) == '.')) {
					return existing[i];
				}
			}
		}
		return null;
	}

	/**
	 * Returns the original if the given member. If the member is already an
	 * original the input is returned. The returned member might not exist
	 * @param member 
	 * @return the original IMember
	 */
	public static IMember toOriginal(IMember member) {
		if (member instanceof IMethod) {
			return toOriginalMethod((IMethod) member);
		}

		return (IMember) member.getPrimaryElement();
		/*
		 * ICompilationUnit cu= member.getCompilationUnit(); if (cu != null &&
		 * cu.isWorkingCopy()) return (IMember)cu.getOriginal(member); return
		 * member;
		 */
	}

	/*
	 * XXX workaround for bug 18568
	 * http://bugs.eclipse.org/bugs/show_bug.cgi?id=18568 to be removed once the
	 * bug is fixed
	 */
	private static IMethod toOriginalMethod(IMethod method) {
		ICompilationUnit cu = method.getCompilationUnit();
		if (cu == null || isPrimary(cu)) {
			return method;
		}
		try {
			// use the workaround only if needed
			if (!method.getElementName().equals(
					method.getDeclaringType().getElementName()))
				return (IMethod) method.getPrimaryElement();

			IType originalType = (IType) toOriginal(method.getDeclaringType());
			IMethod[] methods = originalType.findMethods(method);
			boolean isConstructor = method.isConstructor();
			for (int i = 0; i < methods.length; i++) {
				if (methods[i].isConstructor() == isConstructor)
					return methods[i];
			}
			return null;
		} catch (JavaModelException e) {
			return null;
		}
	}

	// private static boolean PRIMARY_ONLY = false;

	/**
	 * Returns the original cu if the given cu is a working copy. If the cu is
	 * already an original the input cu is returned. The returned cu might not
	 * exist
	 * @param cu 
	 * @return the original compiliation unit
	 */
	public static ICompilationUnit toOriginal(ICompilationUnit cu) {
		// To stay compatible with old version returned null
		// if cu is null
		if (cu == null)
			return cu;
		return cu.getPrimary();
	}

	/**
	 * Returns the original element if the given element is a working copy. If
	 * the cu is already an original the input element is returned. The returned
	 * element might not exist
	 * @param element 
	 * @return element's primary element
	 */
	public static IJavaElement toOriginal(IJavaElement element) {
		return element.getPrimaryElement();
	}

	/**
	 * Returns true if a cu is a primary cu (original or shared working copy)
	 * @param cu 
	 * @return true if cu  is primary
	 */
	public static boolean isPrimary(ICompilationUnit cu) {
		return cu.getOwner() == null;
	}

	/**
	 * http://bugs.eclipse.org/bugs/show_bug.cgi?id=19253
	 * 
	 * Reconciling happens in a separate thread. This can cause a situation
	 * where the Java element gets disposed after an exists test has been done.
	 * So we should not log not present exceptions when they happen in working
	 * copies.
	 * @param exception 
	 * @return true if filter not present
	 */
	public static boolean filterNotPresentException(CoreException exception) {
		if (!(exception instanceof JavaModelException)) {
			return true;
		}
		JavaModelException je = (JavaModelException) exception;
		if (!je.isDoesNotExist()) {
			return true;
		}
		IJavaElement[] elements = je.getJavaModelStatus().getElements();
		for (int i = 0; i < elements.length; i++) {
			IJavaElement element = elements[i];
			ICompilationUnit unit = (ICompilationUnit) element
					.getAncestor(IJavaElement.COMPILATION_UNIT);
			if (unit == null) {
				return true;
			}
			if (!unit.isWorkingCopy()) {
				return true;
			}
		}
		return false;
	}

	/**
	 * @param type
	 * @param pm
	 * @return all supertypes of type
	 * @throws JavaModelException
	 */
	public static IType[] getAllSuperTypes(IType type, IProgressMonitor pm)
			throws JavaModelException {
		// workaround for 23656
		Set types = new HashSet(Arrays.asList(type.newSupertypeHierarchy(pm)
				.getAllSupertypes(type)));
		IType objekt = type.getJavaProject().findType("java.lang.Object");//$NON-NLS-1$
		if (objekt != null) {
			types.add(objekt);
		}
		return (IType[]) types.toArray(new IType[types.size()]);
	}

	/**
	 * @param resourcePath
	 * @param exclusionPatterns
	 * @return true if resourcePath is excluded by exclusion patterns
	 */
	public static boolean isExcludedPath(IPath resourcePath,
			IPath[] exclusionPatterns) {
		char[] path = resourcePath.toString().toCharArray();
		for (int i = 0, length = exclusionPatterns.length; i < length; i++) {
			char[] pattern = exclusionPatterns[i].toString().toCharArray();
			if (CharOperation.pathMatch(pattern, path, true, '/')) {
				return true;
			}
		}
		return false;
	}

	/*

	 * @see IClasspathEntry#getExclusionPatterns
	 */
	/**
	 * Returns whether the given resource path matches one of the exclusion
	 * patterns.
	 * 
	 * @param resourcePath
	 * @param exclusionPatterns
	 * @return true if resourcePath is excluded
	 */
	public static boolean isExcluded(IPath resourcePath,
			char[][] exclusionPatterns) {
		if (exclusionPatterns == null) {
			return false;
		}
		char[] path = resourcePath.toString().toCharArray();
		for (int i = 0, length = exclusionPatterns.length; i < length; i++) {
			if (CharOperation.pathMatch(exclusionPatterns[i], path, true, '/')) {
				return true;
			}
		}
		return false;
	}

	private static Boolean fgIsJDTCore_1_5 = null;

	/**
	 * @return true if JRE 1.5 in enabled.
	 */
	public static boolean isJDTCore_1_5() {
		if (fgIsJDTCore_1_5 == null) {
			fgIsJDTCore_1_5 = JavaCore
					.getDefaultOptions()
					.containsKey(
							"org.eclipse.jdt.core.compiler.problem.unsafeTypeOperation") ? Boolean.TRUE
					: Boolean.FALSE;
		}
		return fgIsJDTCore_1_5.booleanValue();
	}

	/**
	 * Helper method that tests if an classpath entry can be found in a
	 * container. <code>null</code> is returned if the entry can not be found
	 * or if the container does not allows the configuration of source
	 * attachments
	 * 
	 * @param jproject
	 *            The container's parent project
	 * @param containerPath
	 *            The path of the container
	 * @param libPath
	 *            The path of the library to be found
	 * @return IClasspathEntry A classpath entry from the container of
	 *         <code>null</code> if the container can not be modified.
	 * @throws JavaModelException 
	 */
	public static IClasspathEntry getClasspathEntryToEdit(
			IJavaProject jproject, IPath containerPath, IPath libPath)
			throws JavaModelException {
		IClasspathContainer container = JavaCore.getClasspathContainer(
				containerPath, jproject);
		ClasspathContainerInitializer initializer = JavaCore
				.getClasspathContainerInitializer(containerPath.segment(0));
		if (container != null
				&& initializer != null
				&& initializer.canUpdateClasspathContainer(containerPath,
						jproject)) {
			IClasspathEntry[] entries = container.getClasspathEntries();
			for (int i = 0; i < entries.length; i++) {
				IClasspathEntry curr = entries[i];
				IClasspathEntry resolved = JavaCore
						.getResolvedClasspathEntry(curr);
				if (resolved != null && libPath.equals(resolved.getPath())) {
					return curr; // return the real entry
				}
			}
		}
		return null; // attachment not possible
	}
}
