content: /*******************************************************************************
   * Copyright (c) 2000, 2008 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.refactoring.structure;
  
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.List;
  
  import org.eclipse.ltk.core.refactoring.RefactoringStatus;
  import org.eclipse.ltk.core.refactoring.RefactoringStatusContext;
  import org.eclipse.wst.jsdt.core.IField;
  import org.eclipse.wst.jsdt.core.IJavaScriptElement;
  import org.eclipse.wst.jsdt.core.IMember;
  import org.eclipse.wst.jsdt.core.IFunction;
  import org.eclipse.wst.jsdt.core.IType;
  import org.eclipse.wst.jsdt.core.JavaScriptModelException;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.Checks;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.RefactoringCoreMessages;
  import org.eclipse.wst.jsdt.internal.corext.refactoring.base.JavaStatusContext;
  import org.eclipse.wst.jsdt.internal.corext.util.JavaModelUtil;
  import org.eclipse.wst.jsdt.internal.corext.util.Messages;
  
  class MemberCheckUtil {
  	
  	private MemberCheckUtil(){
  		//static only
  	}
  	
  	public static RefactoringStatus checkMembersInDestinationType(IMember[] members, IType destinationType) throws JavaScriptModelException {	
  		RefactoringStatus result= new RefactoringStatus();
  		for (int i= 0; i < members.length; i++) {
  			if (members[i].getElementType() == IJavaScriptElement.METHOD)
  				checkMethodInType(destinationType, result, (IFunction)members[i]);
  			else if (members[i].getElementType() == IJavaScriptElement.FIELD)
  				checkFieldInType(destinationType, result, (IField)members[i]);
  			else if (members[i].getElementType() == IJavaScriptElement.TYPE)
  				checkTypeInType(destinationType, result, (IType)members[i]);
  		}
  		return result;	
  	}
  
  	private static void checkMethodInType(IType destinationType, RefactoringStatus result, IFunction method) throws JavaScriptModelException {
  		IFunction[] destinationTypeMethods= destinationType.getFunctions();
  		IFunction found= findMethod(method, destinationTypeMethods);
  		if (found != null){
  			RefactoringStatusContext context= JavaStatusContext.create(destinationType.getJavaScriptUnit(), found.getSourceRange());
  			String message= Messages.format(RefactoringCoreMessages.MemberCheckUtil_signature_exists, 
  					new String[]{method.getElementName(), JavaModelUtil.getFullyQualifiedName(destinationType)});
  			result.addError(message, context);
  		} else {
  			IFunction similar= Checks.findMethod(method, destinationType);
  			if (similar != null){
  				String message= Messages.format(RefactoringCoreMessages.MemberCheckUtil_same_param_count,
  						 new String[]{method.getElementName(), JavaModelUtil.getFullyQualifiedName(destinationType)});
  				RefactoringStatusContext context= JavaStatusContext.create(destinationType.getJavaScriptUnit(), similar.getSourceRange());
  				result.addWarning(message, context);
  			}										
  		}	
  	}
  	
  	private static void checkFieldInType(IType destinationType, RefactoringStatus result, IField field) throws JavaScriptModelException {
  		IField destinationTypeField= destinationType.getField(field.getElementName());	
  		if (! destinationTypeField.exists())
  			return;
  		String message= Messages.format(RefactoringCoreMessages.MemberCheckUtil_field_exists, 
  				new String[]{field.getElementName(), JavaModelUtil.getFullyQualifiedName(destinationType)});
  		RefactoringStatusContext context= JavaStatusContext.create(destinationType.getJavaScriptUnit(), destinationTypeField.getSourceRange());
  		result.addError(message, context);
  	}
  	
  	private static void checkTypeInType(IType destinationType, RefactoringStatus result, IType type) throws JavaScriptModelException {
  		String typeName= type.getElementName();
  		IType destinationTypeType= destinationType.getType(typeName);
  		if (destinationTypeType.exists()){
  			String message= Messages.format(RefactoringCoreMessages.MemberCheckUtil_type_name_conflict0,  
  					new String[]{typeName, JavaModelUtil.getFullyQualifiedName(destinationType)});
  			RefactoringStatusContext context= JavaStatusContext.create(destinationType.getJavaScriptUnit(), destinationTypeType.getNameRange());
  			result.addError(message, context);
  		} else {
  			//need to check the hierarchy of enclosing and enclosed types
  			if (destinationType.getElementName().equals(typeName)){
  				String message= Messages.format(RefactoringCoreMessages.MemberCheckUtil_type_name_conflict1,  
  						new String[]{JavaModelUtil.getFullyQualifiedName(type)});
  				RefactoringStatusContext context= JavaStatusContext.create(destinationType.getJavaScriptUnit(), destinationType.getNameRange());
  				result.addError(message, context);
  			}
  			if (typeNameExistsInEnclosingTypeChain(destinationType, typeName)){
  				String message= Messages.format(RefactoringCoreMessages.MemberCheckUtil_type_name_conflict2,  
  						new String[]{JavaModelUtil.getFullyQualifiedName(type)});
  				RefactoringStatusContext context= JavaStatusContext.create(destinationType.getJavaScriptUnit(), destinationType.getNameRange());
  				result.addError(message, context);
  			}
  			checkHierarchyOfEnclosedTypes(destinationType, result, type);
  		}
  	}
  
  	private static void checkHierarchyOfEnclosedTypes(IType destinationType, RefactoringStatus result, IType type) throws JavaScriptModelException {
  		IType[] enclosedTypes= getAllEnclosedTypes(type);
  		for (int i= 0; i < enclosedTypes.length; i++) {
  			IType enclosedType= enclosedTypes[i];
  			if (destinationType.getElementName().equals(enclosedType.getElementName())){
  				String message= Messages.format(RefactoringCoreMessages.MemberCheckUtil_type_name_conflict3,  
  						new String[]{JavaModelUtil.getFullyQualifiedName(enclosedType), JavaModelUtil.getFullyQualifiedName(type)});
  				RefactoringStatusContext context= JavaStatusContext.create(destinationType.getJavaScriptUnit(), destinationType.getNameRange());
  				result.addError(message, context);
  			}
  			if (typeNameExistsInEnclosingTypeChain(destinationType, enclosedType.getElementName())){
  				String message= Messages.format(RefactoringCoreMessages.MemberCheckUtil_type_name_conflict4,  
  						new String[]{JavaModelUtil.getFullyQualifiedName(enclosedType), JavaModelUtil.getFullyQualifiedName(type)});
  				RefactoringStatusContext context= JavaStatusContext.create(destinationType.getJavaScriptUnit(), destinationType.getNameRange());
  				result.addError(message, context);
  			}
  		}
  	}
  	
  	private static IType[] getAllEnclosedTypes(IType type) throws JavaScriptModelException {
  		List result= new ArrayList(2);
  		IType[] directlyEnclosed= type.getTypes();
  		result.addAll(Arrays.asList(directlyEnclosed));
  		for (int i= 0; i < directlyEnclosed.length; i++) {
  			IType enclosedType= directlyEnclosed[i];
  			result.addAll(Arrays.asList(getAllEnclosedTypes(enclosedType)));
  		}
  		return (IType[]) result.toArray(new IType[result.size()]);
  	}
  
  	private static boolean typeNameExistsInEnclosingTypeChain(IType type, String typeName){
  		IType enclosing= type.getDeclaringType();
  		while (enclosing != null){
  			if (enclosing.getElementName().equals(typeName))
  				return true;
  			enclosing= enclosing.getDeclaringType();
  		}
  		return false;
  	}
  	
  	/**
  	 * Finds a method in a list of methods. Compares methods by signature
  	 * (only SimpleNames of types), and not by the declaring type.
  	 * @return The found method or <code>null</code>, if nothing found
  	 */
  	public static IFunction findMethod(IFunction method, IFunction[] allMethods) throws JavaScriptModelException {
  		String name= method.getElementName();
  		String[] paramTypes= method.getParameterTypes();
  		boolean isConstructor= method.isConstructor();
  
  		for (int i= 0; i < allMethods.length; i++) {
  			if (JavaModelUtil.isSameMethodSignature(name, paramTypes, isConstructor, allMethods[i]))
  				return allMethods[i];
  		}
  		return null;
  	}
  }
  