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.ui.typehierarchy;
  
  import org.eclipse.jface.viewers.Viewer;
  import org.eclipse.jface.viewers.ViewerComparator;
  import org.eclipse.wst.jsdt.core.Flags;
  import org.eclipse.wst.jsdt.core.IFunction;
  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.internal.corext.util.JavaModelUtil;
  import org.eclipse.wst.jsdt.internal.corext.util.MethodOverrideTester;
  import org.eclipse.wst.jsdt.internal.ui.viewsupport.SourcePositionComparator;
  import org.eclipse.wst.jsdt.ui.JavaScriptElementComparator;
  
  /**
    */
  public abstract class AbstractHierarchyViewerSorter extends ViewerComparator {
  	
  	private static final int OTHER= 1;
  	private static final int CLASS= 2;
  	private static final int ANONYM= 4;
  	
  	private JavaScriptElementComparator fNormalSorter;
  	private SourcePositionComparator fSourcePositonSorter;
  	
  	public AbstractHierarchyViewerSorter() {
  		fNormalSorter= new JavaScriptElementComparator();
  		fSourcePositonSorter= new SourcePositionComparator();
  	}
  	
  	protected abstract ITypeHierarchy getHierarchy(IType type);
  	public abstract boolean isSortByDefiningType();
  	public abstract boolean isSortAlphabetically();
  	
  	
  	protected int getTypeFlags(IType type) throws JavaScriptModelException {
  		return type.getFlags();
  	}
  	
  	/* (non-Javadoc)
  	 * @see org.eclipse.jface.viewers.ViewerSorter#category(java.lang.Object)
  	 */
  	public int category(Object element) {
  		if (element instanceof IType) {
  			IType type= (IType) element;
  			if (type.getElementName().length() == 0) {
  				return ANONYM;
  			}
  
  			return CLASS;
  		}
  		return OTHER;
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.jface.viewers.ViewerSorter#compare(null, null, null)
  	 */
  	public int compare(Viewer viewer, Object e1, Object e2) {
  		if (!isSortAlphabetically() && !isSortByDefiningType()) {
  			return fSourcePositonSorter.compare(viewer, e1, e2);
  		}
  		
  		int cat1= category(e1);
  		int cat2= category(e2);
  
  		if (cat1 != cat2)
  			return cat1 - cat2;
  		
  		if (cat1 == OTHER) { // method or field
  			if (isSortByDefiningType()) {
  				try {
  					IType def1= (e1 instanceof IFunction) ? getDefiningType((IFunction) e1) : null;
  					IType def2= (e2 instanceof IFunction) ? getDefiningType((IFunction) e2) : null;
  					if (def1 != null) {
  						if (def2 != null) {
  							if (!def2.equals(def1)) {
  								return compareInHierarchy(def1, def2);
  							}
  						} else {
  							return -1;						
  						}					
  					} else {
  						if (def2 != null) {
  							return 1;
  						}	
  					}
  				} catch (JavaScriptModelException e) {
  					// ignore, default to normal comparison
  				}
  			}
  			if (isSortAlphabetically()) {
  				return fNormalSorter.compare(viewer, e1, e2); // use appearance pref page settings
  			}
  			return 0;
  		} else if (cat1 == ANONYM) {
  			return 0;
  		} else if (isSortAlphabetically()) {
  			String name1= ((IType) e1).getElementName(); 
  			String name2= ((IType) e2).getElementName(); 
  			return getComparator().compare(name1, name2);
  		}
  		return 0;
  	}
  	
  	private IType getDefiningType(IFunction method) throws JavaScriptModelException {
  		int flags= method.getFlags();
  		if (Flags.isPrivate(flags) || Flags.isStatic(flags) || method.isConstructor()) {
  			return null;
  		}
  	
  		IType declaringType= method.getDeclaringType();
  		if (declaringType != null) {
              ITypeHierarchy hierarchy = getHierarchy(declaringType);
              if (hierarchy != null) {
                  MethodOverrideTester tester = new MethodOverrideTester(declaringType, hierarchy);
                  IFunction res = tester.findDeclaringMethod(method, true);
                  if (res != null) {
                      return res.getDeclaringType();
                  }
              }
          }
  		return null;
  	}
  	
  
  	private int compareInHierarchy(IType def1, IType def2) {
  		if (JavaModelUtil.isSuperType(getHierarchy(def1), def2, def1)) {
  			return 1;
  		} else if (JavaModelUtil.isSuperType(getHierarchy(def2), def1, def2)) {
  			return -1;
  		}
  		String name1= def1.getElementName();
  		String name2= def2.getElementName();
  		
  		return getComparator().compare(name1, name2);
  	}
  
  }
  