alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.editor.ext.java;

import java.lang.reflect.Modifier;
import java.util.Iterator;
import java.util.Comparator;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.HashMap;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.HashSet;
import org.netbeans.editor.SettingsUtil;
import org.netbeans.editor.ext.ExtSettingsDefaults;
import org.netbeans.editor.ext.ExtSettingsNames;
import org.netbeans.editor.ext.java.JCConstructor;

/**
* Java completion support finder
*
* @author Miloslav Metelka
* @version 1.00
*/
// XXX: PERF: when this finder is used as delegate in CompoundFinder
// some operations (like sorting) are useless because the result will be
// sorted once again in CompoundFinder and therefore it should be skipped here.
public class JCBaseFinder extends JavaCompletion.AbstractProvider
    implements JCFinder{

    public static final Comparator CLASS_NAME_COMPARATOR = new DefaultClassNameComparator();
    public static final Comparator INSENSITIVE_CLASS_NAME_COMPARATOR = new InsensitiveClassNameComparator();
    public static final Comparator NATURAL_MEMBER_NAME_COMPARATOR = new NaturalMemberNameComparator(true);
    public static final Comparator INSENSITIVE_NATURAL_MEMBER_NAME_COMPARATOR = new NaturalMemberNameComparator();
    
    private static final int PACKAGE_PRE_ALLOC = 1009;

    private static final int CLASS_PRE_ALLOC = 5003;

    protected JCPackage[] allPackages;

    protected JCClass[] allClassesByName;

    protected HashMap allPackagesMap = new HashMap(PACKAGE_PRE_ALLOC);

    protected HashMap allClassesMap = new HashMap(CLASS_PRE_ALLOC);
    
    /** Current value of these properties. */
    private Boolean oldCaseSensitive = null;
    private Boolean oldNaturalSort = null;
    
    private Object sorted;

    /** Kit class from which for which the settings like case sensitivity etc. will be read */
    private Class kitClass;

    private JCFinder parentFinder;
    
    public JCBaseFinder(Class kitClass) {
        this.kitClass = kitClass;
        this.parentFinder = this;
    }

    // XXX: This is temporary solution which needs rethinking.
    //
    // Background: Each classpath item has its own parser DB with associated
    // JCBaseFinder. For one project are finders compound in CompoundFinder.
    // The compound finder is used for searching and it implements it by delegating.
    // The problem is that individual JCBaseFinders need access to compound finder too,
    // for example for resolving methods and members from all superclasses which
    // can be in different finder, eg. finder for test class which extends 
    // junit.TestCase needs to access finder for junit.jar.
    //
    // Current workaround is method below which is called from CompoundFinder
    // and which sets the finder its parent finder. The parent finder must be 
    // used when global search is needed, see eg. getClassList() method.
    void setParentFinder(JCFinder parentFinder) {
        // XXX: if there are more than one compound finder at one time
        // sharing the same finder (not true now) this code will be broken.
        this.parentFinder = parentFinder;
    }
    
    public Iterator getClasses() {
        return new ArrayList(Arrays.asList(getAllClasses())).iterator();
    }

    public synchronized boolean append(JCClassProvider cp) {
        if (!super.append(cp)) {
            return false;
        }
        return true;
    }

    public synchronized boolean remove(JCClassProvider cp) {
        if (!super.remove(cp)) {
            return false;
        }
        return true;
    }
    
    /** Removes a class from allClassMap 
     * @return true if class already was in allClassMap
     */
    protected boolean removeClass(JCClass cls) {
        invalidate();
        return (allClassesMap.remove(cls.getFullName()) != null);
    }
    
    protected boolean appendClass(JCClass cls) {
        if (!cheapUpdate(cls)) { // try cheap update first
            invalidate(); // reset all if failed
        }
        return true;
    }
    
    public synchronized void reset() {
        allClassesMap.clear();
        invalidate();
    }

    protected void invalidate() {
        allPackagesMap.clear();
        allPackages = null;
        allClassesByName = null;
    }

    public synchronized JCPackage getExactPackage(String packageName) {
        if (allPackages == null) { // not initialized yet
            build();
        }
        return (JCPackage)allPackagesMap.get(packageName);
    }

    public synchronized JCClass getExactClass(String classFullName) {
        return (JCClass)allClassesMap.get(classFullName);
    }

    protected JCPackage[] getAllPackages() {
        if (allPackages == null) {
            build();
        }
        return allPackages;
    }

    protected JCClass[] getAllClassesByName() {
        if (allClassesByName == null) {
            build();
        }
        return allClassesByName;
    }

    protected JCClass[] getAllClasses() {
        JCClass[] allClasses = (JCClass[])getAllClassesByName().clone();
        Arrays.sort(allClasses);
        return allClasses;
    }

    private boolean cheapUpdate(JCClass cls) {
        Object o = allClassesMap.put(cls.getFullName(), cls);

        if (allClassesByName != null && o != null) { // inited and class already there, can do cheap update
            String pkgName = cls.getPackageName();
            JCPackage pkg = (JCPackage)allPackagesMap.get(pkgName);
            if (pkg == null) { // strange - package missing in package map
                return false;
            }
            JCClass[] clist = pkg.getClasses();
            int ind = Arrays.binarySearch(clist, cls);
            if (ind < 0) { // strange - class is missing in the package class list
                return false;
            }
            clist[ind] = cls;

            // Update allClassesByName array - can be more with the same name

            ind = getCaseSensitive() ? Arrays.binarySearch(allClassesByName, cls, CLASS_NAME_COMPARATOR) :
                Arrays.binarySearch(allClassesByName, cls, INSENSITIVE_CLASS_NAME_COMPARATOR);

            // adjust start index
            if (ind < 0) { // not exact match
                ind = -ind - 1;
            }

            // position to start of matching classes
            String name = cls.getName();
            while (ind >= 0 && ind < allClassesByName.length) {
                if (!startsWith(allClassesByName[ind].getName(), name)) {
                    break;
                }
                ind--;
            }
            ind++;

            // replace the matching class in the list
            boolean updated = false;
            while (ind < allClassesByName.length) {
                if (cls.equals(allClassesByName[ind])) {
                    allClassesByName[ind] = cls;
                    updated = true;
                    break;
                }
                if (!name.equals(allClassesByName[ind].getName())) {
                    break;
                }
                ind++;
            }
            return updated;
        }
        return false;
    }

    private void addPackage(JCPackage pkg, boolean force) {
        if (force || !allPackagesMap.containsKey(pkg)) {
            if (pkg.getName().length() == 0) return;
            allPackagesMap.put(pkg.getName(), pkg);
            String name = pkg.getName();
            int i = name.lastIndexOf('.');
            if (i >= 0) {
                addPackage(new JavaCompletion.BasePackage(name.substring(0, i)), false);
            }
        }
    }

    protected void build() {
        // Build class array and class by name array
        JCClass[] allClasses = new JCClass[allClassesMap.size()];
        allClassesByName = new JCClass[allClasses.length];

        Iterator itr = allClassesMap.values().iterator();
        int ind = 0;
        while (itr.hasNext()) {
            allClasses[ind] = (JCClass)itr.next();
            allClassesByName[ind] = allClasses[ind];
            ind++;
        }

        Arrays.sort(allClasses);
        if (getCaseSensitive()){
            Arrays.sort(allClassesByName, CLASS_NAME_COMPARATOR);
            sorted = CLASS_NAME_COMPARATOR;
        }else{
            Arrays.sort(allClassesByName, INSENSITIVE_CLASS_NAME_COMPARATOR);
            sorted = INSENSITIVE_CLASS_NAME_COMPARATOR;
        }

        // Build package array
        allPackagesMap.clear();
        allPackages = JavaCompletion.EMPTY_PACKAGES;

        if (allClasses.length > 0) {
            ArrayList pkgClassList = new ArrayList();
            JCPackage curPkg = new JavaCompletion.BasePackage(allClasses[0].getPackageName());

            for (int i = 0; i < allClasses.length; i++) {
                String pkgName = allClasses[i].getPackageName();
                if (!curPkg.equals(pkgName)) {
                    JCClass classes[] = new JCClass[pkgClassList.size()];
                    pkgClassList.toArray(classes);
                    curPkg.setClasses(classes);
                    pkgClassList.clear();
                    addPackage(curPkg, true);
                    curPkg = new JavaCompletion.BasePackage(pkgName);
                }
                pkgClassList.add(allClasses[i]);
            }
            JCClass classes[] = new JCClass[pkgClassList.size()];
            pkgClassList.toArray(classes);
            curPkg.setClasses(classes);
            addPackage(curPkg, true);

            allPackages = new JCPackage[allPackagesMap.size()];
            itr = allPackagesMap.values().iterator();
            ind = 0;
            while (itr.hasNext()) {
                allPackages[ind] = (JCPackage)itr.next();
                ind++;
            }
        }
        Arrays.sort(allPackages);
    }

    public synchronized List findPackages(String name, boolean exactMatch, boolean subPackages) {
        List ret = new ArrayList();
        if (exactMatch) {
            JCPackage pkg = getExactPackage(name);
            if (pkg != null) {
                ret.add(pkg);
            }
            if (!subPackages) {
                return ret;
            }
        }

        JCPackage packages[] = getAllPackages();
        JCPackage key = new JavaCompletion.BasePackage(name);
        if (!getCaseSensitive()){
            Arrays.sort(packages, INSENSITIVE_CLASS_NAME_COMPARATOR);
        }
        int ind = Arrays.binarySearch(packages, key, (getCaseSensitive()) ? CLASS_NAME_COMPARATOR : INSENSITIVE_CLASS_NAME_COMPARATOR);
        int nameLen = name.length();

        // adjust start index
        if (ind < 0) { // not exact match
            ind = -ind - 1;
        }

        // position to start of matching package names
        while (ind >= 0 && ind < packages.length) {
            if (!startsWith(packages[ind].getName(), name)) {
                break;
            }
            ind--;
        }
        ind++;

        // add the matching packages to the list
        int reqDotCount = key.getDotCount();
        while (ind < packages.length) {
            String pkgName = packages[ind].getName();

            if (!startsWith(pkgName, name)) {
                break;
            }

            if (exactMatch ?
                    (pkgName.length() > nameLen && pkgName.charAt(nameLen) == '.')
                    : (subPackages || packages[ind].getDotCount() == reqDotCount)
               ) {
                ret.add(packages[ind]);
            }

            ind++;
        }

        return ret;
    }

    /** Find classes by name and possibly in some package
    * @param pkg package where the classes should be searched for. It can be null
    * @param begining of the name of the class. The package name must be omitted.
    * @param exactMatch whether the given name is the exact requested name
    *   of the class or not.
    * @return list of the matching classes
    */
    public synchronized List findClasses(JCPackage pkg, String name, boolean exactMatch) {
        List ret = new ArrayList();
        JCClass[] classes;
        int ind;
        JCClass key = new JavaCompletion.SimpleClass(name, ""); // NOI18N
        int nameLen = name.length();
        if (pkg != null) {
            classes = pkg.getClasses();
            if (!getCaseSensitive()){
                Arrays.sort(classes, INSENSITIVE_CLASS_NAME_COMPARATOR);
            }
        } else { // pkg is null
            classes = getAllClassesByName();
        }
        if (getCaseSensitive()){
            if (!CLASS_NAME_COMPARATOR.equals(sorted)) {
                Arrays.sort(classes, CLASS_NAME_COMPARATOR);
                sorted = CLASS_NAME_COMPARATOR;
            }
            ind = Arrays.binarySearch(classes, key, CLASS_NAME_COMPARATOR);
        }else{
            if (!INSENSITIVE_CLASS_NAME_COMPARATOR.equals(sorted)){
                Arrays.sort(classes, INSENSITIVE_CLASS_NAME_COMPARATOR);                
                sorted = INSENSITIVE_CLASS_NAME_COMPARATOR;
            }
            ind = Arrays.binarySearch(classes, key, INSENSITIVE_CLASS_NAME_COMPARATOR);
        }
        

        // adjust start index
        if (ind < 0) { // not exact match
            ind = -ind - 1;
        }

        // position to start of matching classes
        while (ind >= 0 && ind < classes.length) {
            if (!startsWith(classes[ind].getName(), name)) {
                break;
            }
            ind--;
        }
        ind++;

        // add the matching classes to the list
        while (ind < classes.length) {
            String className = classes[ind].getName();
            
            if (!startsWith(className, name)) {
                break;
            }

            if (!exactMatch || className.length() == nameLen) {
                if (showDeprecated() || !JCUtilities.isDeprecated(classes[ind])){
                    ret.add(classes[ind]);
                }
            }
            ind++;
        }
        Collections.sort(ret, getNaturalSort() ? INSENSITIVE_CLASS_NAME_COMPARATOR : CLASS_NAME_COMPARATOR);        
        return ret;
    }

    /** Get outer classes to search
    * the fields and methods there. The original class is added
    * as the first member of the resulting list.
    */
    private List getOuterClasses(JCClass cls) {
        ArrayList outers = new ArrayList();
        outers.add(cls);
        int lastDotInd = cls.getName().lastIndexOf('.');
        while (lastDotInd >= 0) {
            int pkgLen = cls.getPackageName().length();
            String fullName = cls.getFullName().substring(0,
                              ((pkgLen > 0) ? (pkgLen + 1) : 0) + lastDotInd);
            cls = parentFinder.getExactClass(fullName);
            if (cls != null) {
                if (showDeprecated() || !JCUtilities.isDeprecated(cls)){
                    outers.add(cls);
                }
                lastDotInd = cls.getName().lastIndexOf('.');                
            } else {
                break;
            }
        }
        return outers;
    }

    /** Find fields by name in a given class.
    * @param cls class which is searched for the fields.
    * @param name start of the name of the field
    * @param exactMatch whether the given name of the field is exact
    * @param staticOnly whether search for the static fields only
    * @return list of the matching fields
    */
    public synchronized List findFields(JCClass cls, String name, boolean exactMatch,
                           boolean staticOnly, boolean inspectOuterClasses) {
        return findFields(cls.getPackageName(), cls, name, exactMatch, staticOnly, inspectOuterClasses);
    }
    
    synchronized List findFields(String curPkg, JCClass cls, String name, boolean exactMatch,
                           boolean staticOnly, boolean inspectOuterClasses) {
        TreeSet ts;
        if (getNaturalSort()){
            ts = new TreeSet(INSENSITIVE_NATURAL_MEMBER_NAME_COMPARATOR);
        }else{
            ts = new TreeSet(NATURAL_MEMBER_NAME_COMPARATOR);
        }
        
        List clsList = getClassList(cls);
        String pkgName = curPkg;
        HashSet ifaces = new HashSet(); // The set for temporal storage of all implemented interfaces
        
        JCClass innerClass = cls;

        for (int i = clsList.size() - 1; i >= 0; i--) {
            cls = parentFinder.getExactClass(((JCClass)clsList.get(i)).getFullName());
            if (cls != null) {
                // remember all the interfaces along the way through hierarchy
                if (cls.isInterface()) ifaces.add(cls); //bugfix of #19615
                ifaces.addAll( JCUtilities.getAllInterfaces(parentFinder, cls) ); 
                boolean difPkg = pkgName != null && !cls.getPackageName().equals(pkgName);
                List outerList = (i == 0 && inspectOuterClasses && cls.getName().indexOf('.') >= 0)
                                 ? getOuterClasses(cls) : null;
                int outerInd = (outerList != null) ? (outerList.size() - 1) : -1;
                do {
                    if (outerInd >= 0) {
                        cls = (JCClass)outerList.get(outerInd--);
                    }
                    JCField[] fields = cls.getFields();
                    for (int j = 0; j < fields.length; j++) {
                        JCField fld = fields[j];
                        int mods = fld.getModifiers();
                        if ((staticOnly && (mods & Modifier.STATIC) == 0)
                                || (i > 0 && (mods & Modifier.PRIVATE) != 0)
                                || (difPkg && (mods & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0)
                                || ((outerInd >-1) && ((innerClass.getModifiers() & Modifier.STATIC) != 0 ) && ((mods & Modifier.STATIC) == 0))
                           ) {
                            continue;
                        }
                        if (exactMatch) {
                            if (!fld.getName().equals(name)) {
                                continue;
                            }
                        } else {
                            if (!startsWith(fld.getName(), name)) {
                                continue;
                            }
                        }
                        
                        if (innerClass.equals(cls) && outerInd == -1) {
                            fld = new JavaCompletion.BaseField(cls, fld.getName(), fld.getType(),
                                (fld.getModifiers() | JavaCompletion.LOCAL_MEMBER_BIT));
                        }
                        
                        if (showDeprecated() || !JCUtilities.isDeprecated(fld)){
                            ts.add(fld);
                        }
                    }
                } while (outerInd >= 0);
            }
        }
        // add ALL known fields from interfaces, ALL as they are public static
        for( Iterator it = ifaces.iterator(); it.hasNext(); ) {
            cls = parentFinder.getExactClass(((JCClass)it.next()).getFullName());
            if (cls != null) {
                JCField[] fields = cls.getFields();
                for (int j = 0; j < fields.length; j++) {
                    JCField fld = fields[j];
                    if( exactMatch ? !fld.getName().equals(name)
                                   : !startsWith(fld.getName(), name) ) continue;

                    if (showDeprecated() || !JCUtilities.isDeprecated(fld)){
                        ts.add(fld);
                    }
                }
            }
        }
        
        if (staticOnly){
            if((exactMatch && "class".equals(name)) || (!exactMatch && startsWith("class", name))){ //NOI18N
                JCField field = new JavaCompletion.BaseField(JavaCompletion.CLASS_CLASS, "class", //NOI18N
                JavaCompletion.CLASS_TYPE, Modifier.PUBLIC);
                ts.add(field);
            }
        }
        
        return new ArrayList(ts);
    }

    
    /** Find methods by name in a given class.
    * @param cls class which is searched for the methods.
    * @param name start of the name of the method
    * @param exactMatch whether the given name of the method is exact
    * @param staticOnly whether search for the static methods only
    * @return list of the matching methods
    */
    public synchronized List findMethods(JCClass cls, String name, boolean exactMatch,
                            boolean staticOnly, boolean inspectOuterClasses) {
        return findMethods(cls.getPackageName(), cls, name, exactMatch, staticOnly, inspectOuterClasses);
    }
    
    synchronized List findMethods(String curPkg, JCClass cls, String name, boolean exactMatch,
                            boolean staticOnly, boolean inspectOuterClasses) {
        TreeSet ts;
        if (getNaturalSort()){
            ts = new TreeSet(INSENSITIVE_NATURAL_MEMBER_NAME_COMPARATOR);
        }else{
            ts = new TreeSet();
        }

        List clsList = getClassList(cls);
        String pkgName = curPkg;
        JCClass innerClass = cls;

        for (int i = clsList.size() - 1; i >= 0; i--) {
            cls = parentFinder.getExactClass(((JCClass)clsList.get(i)).getFullName());
            if (cls != null) {
                boolean difPkg = !cls.getPackageName().equals(pkgName);
                List outerList = (i == 0 && inspectOuterClasses && cls.getName().indexOf('.') >= 0)
                                 ? getOuterClasses(cls) : null;
                int outerInd = (outerList != null) ? (outerList.size() - 1) : -1;
                do {
                    if (outerInd >= 0) {
                        cls = (JCClass)outerList.get(outerInd--);
                    }
                    
                    JCMethod[] methods = cls.getMethods();
                    for (int j = 0; j < methods.length; j++) {
                        JCMethod mtd = methods[j];
                        int mods = mtd.getModifiers();
                        if ((staticOnly && (mods & Modifier.STATIC) == 0)
                                || (i > 0 && (mods & Modifier.PRIVATE) != 0)
                                || (difPkg && (mods & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0)
                                || ((outerInd >-1) && ((innerClass.getModifiers() & Modifier.STATIC) != 0 ) && ((mods & Modifier.STATIC) == 0))
                           ) {
                            continue;
                        }
                        if (exactMatch) {
                            if (!mtd.getName().equals(name)) {
                                continue;
                            }
                        } else { // match begining
                            if (!startsWith(mtd.getName(), name)) {
                                continue;
                            }
                        }

                        // override the method from superclass (throwing exceptions could differ)
                        if (ts.contains(mtd)) ts.remove(mtd);
                        
                        if (innerClass.equals(cls) && outerInd == -1) {
                            mtd = new JavaCompletion.BaseMethod(cls, mtd.getName(), (mtd.getModifiers() | JavaCompletion.LOCAL_MEMBER_BIT),
                                mtd.getReturnType(), mtd.getParameters(), mtd.getExceptions());
                        }
                                                
                        if (showDeprecated() || !JCUtilities.isDeprecated(mtd)){
                            ts.add(mtd);
                        }
                    }
                } while (outerInd >= 0);
            }
        }

        return new ArrayList(ts);
    }

    private List getClassList(JCClass cls) {
        boolean deprecated=true;
        cls = getExactClass(cls.getFullName());
        List ret;
        if (cls != null) {
            if (cls.isInterface()){
                ret = JCUtilities.getAllInterfaces(parentFinder, cls, deprecated);
                // #16252 it is legal to call methods for java.lang.Object from an interface
                ret.add(JavaCompletion.OBJECT_CLASS); 
            } else {
                ret = JCUtilities.getSuperclasses(parentFinder, cls);
                if ((cls.getModifiers() & Modifier.ABSTRACT) != 0){
                    // in the case of abstract implementor of interface
                    ret.addAll(JCUtilities.getAllInterfaces(parentFinder, cls, deprecated));
                }
            }

            ret.add(0, cls);

        } else { // class not found
            ret = new ArrayList(); // return empty list
        }

        return ret;
    }

    public String dumpClasses() {
        StringBuffer sb = new StringBuffer(8192); // expect huge growth
        JCClass[] ac = getAllClasses();
        for (int i = 0; i < ac.length; i++) {
            sb.append(JCUtilities.dumpClass(ac[i]));
            sb.append("\n\n"); // NOI18N
        }
        return sb.toString();
    }

    private boolean getCaseSensitive() {
        boolean b = SettingsUtil.getBoolean(kitClass,
            ExtSettingsNames.COMPLETION_CASE_SENSITIVE,
            ExtSettingsDefaults.defaultCompletionCaseSensitive);
        if (oldCaseSensitive != null && oldCaseSensitive.booleanValue() != b) {
            invalidate();
        }
        oldCaseSensitive = Boolean.valueOf(b);
        return b;
    }
    
    private boolean getNaturalSort() {
        boolean b = SettingsUtil.getBoolean(kitClass,
            ExtSettingsNames.COMPLETION_NATURAL_SORT,
            ExtSettingsDefaults.defaultCompletionNaturalSort);
        if (oldNaturalSort != null && oldNaturalSort.booleanValue() != b) {
            invalidate();
        }
        oldNaturalSort = Boolean.valueOf(b);
        return b;
    }
    
    boolean showDeprecated() {
        return SettingsUtil.getBoolean(kitClass,
            ExtSettingsNames.SHOW_DEPRECATED_MEMBERS,
            ExtSettingsDefaults.defaultShowDeprecatedMembers);
    }
    
    private boolean startsWith(String theString, String prefix){
        return getCaseSensitive() ? theString.startsWith(prefix) :
            theString.toLowerCase().startsWith(prefix.toLowerCase());
    }


    public static final class DefaultClassNameComparator implements Comparator {

        public int compare(Object o1, Object o2) {
            if (o1 == o2) {
                return 0;
            }
            if ((o1 instanceof JCClass) && (o2 instanceof JCClass)){
                return ((JCClass)o1).getName().compareTo(((JCClass)o2).getName());
            }
            if ((o1 instanceof JCPackage) && (o2 instanceof JCPackage)){
                return ((JCPackage)o1).getName().compareTo(((JCPackage)o2).getName());
            }
            
            return 0;
        }

    }

    
    public static final class InsensitiveClassNameComparator implements Comparator {

        public int compare(Object o1, Object o2) {
            if (o1 == o2) {
                return 0;
            }
            if ((o1 instanceof JCClass) && (o2 instanceof JCClass)){
                return ((JCClass)o1).getName().compareToIgnoreCase(((JCClass)o2).getName());
            }
            if ((o1 instanceof JCPackage) && (o2 instanceof JCPackage)){
                return ((JCPackage)o1).getName().compareToIgnoreCase(((JCPackage)o2).getName());
            }
            
            return 0;
        }

    }
    
    public static final class NaturalMemberNameComparator implements Comparator {

        private boolean sensitive;
        
        public NaturalMemberNameComparator() {
            this(false);
        }
        
        private NaturalMemberNameComparator(boolean sensitive) {
            this.sensitive = sensitive;
        }
        
        public int compare(Object o1, Object o2) {
            if (o1 == o2) {
                return 0;
            }
            if (o1 instanceof JCMethod && o2 instanceof JCMethod){
                JCMethod met1 = (JCMethod)o1;
                JCMethod met2 = (JCMethod)o2;
                int order = sensitive ?
                    met1.getName().compareTo(met2.getName()) :
                    met1.getName().compareToIgnoreCase(met2.getName());
                if (order == 0 ){
                    JCParameter param1[] = met1.getParameters();
                    JCParameter param2[] = met2.getParameters();

                    int commonCnt = Math.min(param1.length, param2.length);
                    for (int i = 0; i < commonCnt; i++) {
                        order = sensitive ?
                            param1[i].getType().getClazz().getName().compareTo(param2[i].getType().getClazz().getName()) :
                            param1[i].getType().getClazz().getName().compareToIgnoreCase(param2[i].getType().getClazz().getName());
 
                        if (order != 0) {
                            return order;
                        }
                    }
                    order = param1.length - param2.length;
                }
                
                //do not allow methods merge
                int sameName = met1.getName().compareTo(met2.getName());
                if (order == 0 && sameName != 0) order = sameName;
                    
                return order;
            }
            if (o1 instanceof JCField && o2 instanceof JCField){
                JCField fld1 = (JCField)o1;
                JCField fld2 = (JCField)o2;
                int order = sensitive ? 
                    fld1.getName().compareTo(fld2.getName()) :
                    fld1.getName().compareToIgnoreCase(fld2.getName());
                    
                //do not allow fields merge
                int sameName = fld1.getName().compareTo(fld2.getName());
                if (order == 0 && sameName != 0) order = sameName;
                
                return order;
            }
            
            return 0;
        }

    }

}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.