|
What this is
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 |
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.