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-2004 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.editor.ext.java;

import java.awt.Component;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.HashMap;
import java.util.TreeSet;
import javax.swing.text.JTextComponent;
import javax.swing.text.BadLocationException;
import org.netbeans.editor.Formatter;
import org.netbeans.editor.BaseDocument;
import org.netbeans.editor.SyntaxSupport;
import org.netbeans.editor.TokenID;
import org.netbeans.editor.ext.CompletionQuery;
import org.netbeans.editor.ext.ExtFormatter;

/**
* Java completion support finder
*
* @author Miloslav Metelka
* @version 1.00
*/

abstract public class JavaCompletionQuery implements CompletionQuery {

    private BaseDocument baseDocument;
    
    private boolean java15;
    
    private static JCItemFactory itemFactory;

    // the only purpose of this method is that NbJavaCompletionQuery
    // can use it to retrieve document's fileobject and create correct
    // JCFinder with the correct classpath of project to which the file belongs
    protected BaseDocument getBaseDocument(){
        return baseDocument;
    }
    
    abstract protected JCFinder getFinder();
    
    
    public JavaCompletionQuery(){
        super();
        initFactory();
    }
    
    protected void initFactory(){
        setJCItemFactory(new JavaCompletionQuery.DefaultJCItemFactory());        
    }
    
    /**
     * Set whether expression parser should treat the underlying document
     * as using Java 1.5 features.
     *
     * @param java15 true to parse expression as being in java 1.5 syntax.
     */
    protected void setJava15(boolean java15) {
        this.java15 = java15;
    }
    
    public CompletionQuery.Result query(JTextComponent component, int offset,
                                        SyntaxSupport support) {
        return query(component, offset, support, false);
    }

    /** Perform the query on the given component. The query usually
    * gets the component's document, the caret position and searches back
    * to find the last command start. Then it inspects the text up to the caret
    * position and returns the result.
    * @param component the component to use in this query.
    * @param offset position in the component's document to which the query will
    *   be performed. Usually it's a caret position.
    * @param support syntax-support that will be used during resolving of the query.
    * @param openingSource whether the query is performed to open the source file.
    *  The query tries to return exact matches if this flag is true
    * @return result of the query or null if there's no result.
    */
    public CompletionQuery.Result query(JTextComponent component, int offset,
                                        SyntaxSupport support, boolean openingSource) {
        BaseDocument doc = (BaseDocument)component.getDocument();
        
        // remember document here. it is accessible by getBaseDocument()
        // method for subclasses of JavaCompletionQuery, ie. NbJavaCompletionQuery
        baseDocument = doc;
        
        JavaSyntaxSupport sup = (JavaSyntaxSupport)support.get(JavaSyntaxSupport.class);
        CompletionQuery.Result ret = null;

        try {
            // find last separator position
            int lastSepOffset = sup.getLastCommandSeparator(offset);
            JCTokenProcessor tp = new JCTokenProcessor(offset);
            tp.setJava15(java15);

            boolean cont = true;
            while (cont) {
                sup.tokenizeText(tp, lastSepOffset + 1, offset, true);
                if (cont = tp.isStopped()) {
                    lastSepOffset = sup.findMatchingBlock(tp.getCurrentOffest(), true)[0];
                }
            }

            // Check whether there's an erroneous token state under the cursor
            boolean errState = false;
            TokenID lastValidTokenID = tp.getLastValidTokenID();
            if (lastValidTokenID != null) {
                switch (lastValidTokenID.getNumericID()) {
                case JavaTokenContext.MUL_ID:
                    errState = true;
                    break;
                case JavaTokenContext.BLOCK_COMMENT_ID:
                    if (tp.getLastValidTokenText() == null
                            || !tp.getLastValidTokenText().endsWith("*/") // NOI18N
                    ) {
                        errState = true;
                    }
                    break;

                case JavaTokenContext.LINE_COMMENT_ID:
                    errState = true;
                    break;
                }
            }

            if (!errState) {
                // refresh classes info before querying
                sup.refreshClassInfo();

                JCExpression exp = tp.getResultExp();
                ret = getResult(component, sup, openingSource, offset, exp);
            }
        } catch (BadLocationException e) {
            e.printStackTrace();
        }

        return ret;
    }

    protected CompletionQuery.Result getResult(JTextComponent component, JavaSyntaxSupport sup, boolean openingSource, int offset, JCExpression exp) {
        Context ctx = new Context(component, sup, openingSource, offset, getFinder());
        ctx.resolveExp(exp);
        return ctx.result;
    }

    /** Finds the fields, methods and the inner classes.
    */
    static List findFieldsAndMethods(JCFinder finder, String curPkg, JCClass cls, String name,
                                     boolean exactMatch, boolean staticOnly, boolean inspectOuterClasses) {
        // Find inner classes
        List ret = new ArrayList();
        if (staticOnly) {
            JCPackage pkg = finder.getExactPackage(cls.getPackageName());
            if (pkg != null) {
                ret = finder.findClasses(pkg, cls.getName() + '.' + name, false);
            }
        }

        // XXX: this is hack, we should rather create JCFinder2 iface with findFields,
        // findMethods methods accepting current package parameter
        if (finder instanceof JCBaseFinder) {
            // Add fields
            ret.addAll(((JCBaseFinder)finder).findFields(curPkg, cls, name, exactMatch, staticOnly, inspectOuterClasses));
            // Add methods
            ret.addAll(((JCBaseFinder)finder).findMethods(curPkg, cls, name, exactMatch, staticOnly, inspectOuterClasses));
        } else {
            // Add fields
            ret.addAll(finder.findFields(cls, name, exactMatch, staticOnly, inspectOuterClasses));
            // Add methods
            ret.addAll(finder.findMethods(cls, name, exactMatch, staticOnly, inspectOuterClasses));
        }
        
        return ret;
    }

    static class Context {

        /** Text component */
        private JTextComponent component;

        /** Syntax support for the given document */
        private JavaSyntaxSupport sup;

        /** Whether the query is performed to open the source file. It has slightly
        * different handling in some situations.
        */
        private boolean openingSource;

        /** End position of the scanning - usually the caret position */
        private int endOffset;

        /** If set to true true - find the type of the result expression.
        * It's stored in the lastType variable or lastPkg if it's a package.
        * The result variable is not populated.
        * False means that the code completion output should be collected.
        */
        private boolean findType;

        /** Whether currently scanning either the package or the class name
        * so the results should limit the search to the static fields and methods.
        */
        private boolean staticOnly = true;

        /** Last package found when scanning dot expression */
        private JCPackage lastPkg;

        /** Last type found when scanning dot expression */
        private JCType lastType;

        /** Result list when code completion output is generated */
        private JavaResult result;

        /** Helper flag for recognizing constructors */
        private boolean isConstructor;

        /** Finder associated with this Context. */
        private JCFinder finder;

        public Context(JTextComponent component,
                       JavaSyntaxSupport sup, boolean openingSource, int endOffset,
                       JCFinder finder) {
            this.component = component;
            this.sup = sup;
            this.openingSource = openingSource;
            this.endOffset = endOffset;
            this.finder= finder;
        }

        public void setFindType(boolean findType) {
            this.findType = findType;
        }

        protected Object clone() {
            return new Context(component, sup, openingSource, endOffset, finder);
        }

        /*    private List getBaseHelp(String baseName) {
              if (openingSource) {
                JCFinder finder = JavaCompletion.getFinder();
                List res = finder.findPackages(baseName, false, false); // find all subpackages
                if (res == null) {
                  res = new ArrayList();
                }

                if (baseName != null && baseName.length() > 0) {
                  res.addAll(finder.findClasses(null, baseName, false)); // add matching classes
                }
                return res;
              }
              return null;
            }

            private JavaResult getBaseHelpResult(String baseName, JCExpression exp) {
              List res = getBaseHelp(baseName);
              if (res != null && exp != null) {
                return new JavaResult(res, formatName(baseName, true),
                    exp, exp.getTokenOffset(0), 0, 0);
              }
              return null;
            }
        */

        private String formatName(String name, boolean appendStar) {
            return (name != null) ? (appendStar ? (name + '*') : name)
       : (appendStar ? "*" : ""); // NOI18N

        }

        private String formatType(JCType type, boolean useFullName,
                                  boolean appendDot, boolean appendStar) {
            StringBuffer sb = new StringBuffer();
            if (type != null) {
                sb.append(type.format(useFullName));
            }
            if (appendDot) {
                sb.append('.');
            }
            if (appendStar) {
                sb.append('*');
            }
            return sb.toString();
        }

        private JCType resolveType(JCExpression exp) {
            Context ctx = (Context)clone();
            ctx.setFindType(true);
            JCType typ = null;
            if (ctx.resolveExp(exp)) {
                typ = ctx.lastType;
            }
            return typ;
        }

        boolean resolveExp(JCExpression exp) {
            boolean lastDot = false; // dot at the end of the whole expression?
            boolean ok = true;

            if (exp.getExpID() == JCExpression.IMPORT) { // import statement
                exp = exp.getParameterCount() == 2 ? exp.getParameter(1) : exp.getParameter(0);
            }

            switch (exp.getExpID()) {
            case JCExpression.DOT_OPEN: // Dot expression with the dot at the end
                lastDot = true;
                // let it flow to DOT
            case JCExpression.DOT: // Dot expression
                int parmCnt = exp.getParameterCount(); // Number of items in the dot exp

                for (int i = 0; i < parmCnt && ok; i++) { // resolve all items in a dot exp
                    ok = resolveItem(exp.getParameter(i), (i == 0),
                                     (!lastDot && i == parmCnt - 1)
                                    );
                }

                if (ok && lastDot) { // Found either type or package help
                    // Need to process dot at the end of the expression
                    int tokenCntM1 = exp.getTokenCount() - 1;
                    int substPos = exp.getTokenOffset(tokenCntM1) + exp.getTokenLength(tokenCntM1);
                    if (lastType != null) { // Found type
                        JCClass cls;
                        if (lastType.getArrayDepth() == 0) { // Not array
                            cls = lastType.getClazz();
                        } else { // Array of some depth
                            cls = JavaCompletion.OBJECT_CLASS_ARRAY; // Use Object in this case
                        }
                        List res;
                        if (openingSource) {
                            res = new ArrayList();
                            res.add(lastType.getClazz());
                        } else { // not source-help
                            JCClass curCls = sup.getClass(exp.getTokenOffset(tokenCntM1));
                            res = findFieldsAndMethods(finder, curCls == null ? null : curCls.getPackageName(), cls, "", false, staticOnly, false); // NOI18N
                        }
                        // Get all fields and methods of the cls
                        result = new JavaResult(component, res, formatType(lastType, true, true, true),
                                                exp, substPos, 0, cls.getName().length() + 1);
                    } else { // Found package (otherwise ok would be false)
                        String searchPkg = lastPkg.getName() + '.';
                        List res;
                        if (openingSource) {
                            res = new ArrayList();
                            res.add(lastPkg); // return only the package
                        } else {
                            res = finder.findPackages(searchPkg, false, false); // find all subpackages

                            String text = null;
                            try {
                                int firstTokenIdx = exp.getTokenOffset(0);
                                int cmdStartIdx = sup.getLastCommandSeparator(firstTokenIdx);
                                if (cmdStartIdx < 0) {
                                    text = sup.getDocument().getText(0, firstTokenIdx);
                                    cmdStartIdx = text.lastIndexOf(0x0A);
                                    if (cmdStartIdx != -1) {
                                        text = text.substring(cmdStartIdx + 1);
                                    }
                                } else {
                                    text = sup.getDocument().getText(cmdStartIdx, firstTokenIdx - cmdStartIdx);
                                }
                            } catch (BadLocationException e) {
                                // ignore and provide full list of items
                            }

                            if (text != null && -1 == text.indexOf("package")) { //NOI18N
                                res.addAll(finder.findClasses(lastPkg, "", false)); // package classes
                            }
                        }
                        result = new JavaResult(component, res, searchPkg + '*',
                                                exp, substPos, 0, 0);
                    }
                }
                break;

            case JCExpression.NEW: // 'new' keyword
                List res = finder.findClasses(null, "", false); // Find all classes by name // NOI18N
                result = new JavaResult(component, res, "*", exp, endOffset, 0, 0); // NOI18N
                break;

            default: // The rest of the situations is resolved as a singleton item
                ok = resolveItem(exp, true, true);
                break;
            }

            return ok;
        }

        /** Resolve one item from the expression connected by dots.
        * @param item expression item to resolve
        * @param first whether this expression is the first one in a dot expression
        * @param last whether this expression is the last one in a dot expression
        */
        boolean resolveItem(JCExpression item, boolean first, boolean last) {
            boolean cont = true; // whether parsing should continue or not
            boolean methodOpen = false; // helper flag for unclosed methods

            switch (item.getExpID()) {
            case JCExpression.CONSTANT: // Constant item
                if (first) {
                    lastType = JavaCompletion.getPredefinedType(item.getType()); // Get the constant type
                    staticOnly = false;
                } else { // Not the first item in a dot exp
                    cont = false; // impossible to have constant inside the expression
                }
                break;

            case JCExpression.VARIABLE: // Variable or special keywords
                switch (item.getTokenID(0).getNumericID()) {
                    case JavaTokenContext.THIS_ID: // 'this' keyword
                        if (first) { // first item in expression
                            JCClass cls = sup.getClass(item.getTokenOffset(0));
                            if (cls != null) {
                                lastType = JavaCompletion.getType(cls, 0);
                                staticOnly = false;
                            }
                        } else { // 'something.this'
                            staticOnly = false;
                        }
                        break;

                    case JavaTokenContext.SUPER_ID: // 'super' keyword
                        if (first) { // only allowed as the first item
                            JCClass cls = sup.getClass(item.getTokenOffset(0));
                            if (cls != null) {
                                cls = finder.getExactClass(cls.getFullName());
                                if (cls != null) {
                                    cls = cls.getSuperclass();
                                    if (cls != null) {
                                        lastType = JavaCompletion.getType(cls, 0);
                                        staticOnly = false;
                                    }
                                }
                            }
                        } else {
                            cont = false;
                        }
                        break;

                    case JavaTokenContext.CLASS_ID: // 'class' keyword
                        if (!first) {
                            lastType = JavaCompletion.CLASS_TYPE;
                            staticOnly = false;
                        } else {
                            cont = false;
                        }
                        break;

                    default: // Regular constant
                        String var = item.getTokenText(0);
                        int varPos = item.getTokenOffset(0);
                        if (first) { // try to find variable for the first item
                            if (last && !findType) { // both first and last item
                                //List res = new ArrayList();
                                List res = new ArrayList();
                                JCClass cls = sup.getClass(varPos); // get document class
                                if (cls != null) {
                                    res.addAll(findFieldsAndMethods(finder, cls.getPackageName(), cls, var, false,
                                                                    sup.isStaticBlock(varPos), true));
                                }
                                if (var.length() > 0 || !openingSource) {
                                    res.addAll(finder.findPackages(var, false, false)); // add matching packages
                                    if (var.length() > 0) { // if at least one char
                                        res.addAll(finder.findClasses(null, var, false)); // add matching classes
                                        if (cls!=null){
                                            // add matching inner classes too
                                            JCPackage pkg = finder.getExactPackage(cls.getPackageName());
                                            List lst = finder.findClasses(pkg, cls.getName()+"."+var, false); // NOI18N
                                            for (int i=0; i0 && !res.contains(iCls)){ // NOI18N
                                                res.add(iCls);
                                            }
                                        }
                                    }
                                    
                                }
                                result = new JavaResult(component, res, var + '*', item, 0);
                            } else { // not last item or finding type
                                lastType = (JCType)sup.findType(var, varPos);
                                if (lastType != null) { // variable found
                                    staticOnly = false;
                                } else { // no variable found
                                    lastPkg = finder.getExactPackage(var); // try package
                                    if (lastPkg == null) { // not package, let's try class name
                                        JCClass cls = sup.getClassFromName(var, true);
                                        if (cls != null) {
                                            lastType = JavaCompletion.getType(cls, 0);
                                        } else { // class not found
                                            cont = false;
                                        }
                                    }
                                }
                            }
                        } else { // not the first item
                            if (lastType != null) { // last was type
                                if (findType || !last) {
                                    boolean inner = false;
                                    int ad = lastType.getArrayDepth();
                                    if (staticOnly && ad == 0) { // can be inner class
                                        JCClass cls = finder.getExactClass(lastType.getClazz().getFullName() + "." + var); // NOI18N
                                        if (cls != null) {
                                            lastType = JavaCompletion.getType(cls, 0);
                                            inner = true;
                                        }
                                    }

                                    if (!inner) { // not inner class name
                                        if (ad == 0) { // zero array depth
                                            List fldList = finder.findFields(lastType.getClazz(), var, true, staticOnly, false);
                                            if (fldList.size() > 0) { // match found
                                                JCField fld = (JCField)fldList.get(0);
                                                lastType = fld.getType();
                                                staticOnly = false;
                                            } else { // no match found
                                                lastType = null;
                                                cont = false;
                                            }
                                        } else { // array depth > 0 but no array dereference
                                            cont = false;
                                        }
                                    }
                                } else { // last and searching for completion output
                                    JCClass curCls = sup.getClass(varPos);
                                    JCClass cls;
                                    if (lastType.getArrayDepth() == 0) { // Not array
                                        cls = lastType.getClazz();
                                    } else { // Array of some depth
                                        cls = JavaCompletion.OBJECT_CLASS_ARRAY; // Use Object in this case
                                    }
                                    result = new JavaResult(
                                                 component,
                                                 findFieldsAndMethods(finder, curCls == null ? null : curCls.getPackageName(), cls, var, false, staticOnly, false),
                                                 lastType.format(false) + '.' + var + '*',
                                                 item,
                                                 cls.getName().length() + 1);
                                }
                            } else { // currently package
                                String searchName = lastPkg.getName() + '.' + var;
                                if (findType || !last) {
                                    lastPkg = finder.getExactPackage(searchName);
                                    if (lastPkg == null) { // package doesn't exist
                                        JCClass cls = finder.getExactClass(searchName);
                                        if (cls != null) {
                                            lastType = JavaCompletion.getType(cls, 0);
                                        } else {
                                            lastType = null;
                                            cont = false;
                                        }
                                    }
                                } else { // last and searching for completion output
                                    if (last) { // get all matching fields/methods/packages
                                        String searchPkg = lastPkg.getName() + '.' + var;
                                        List res = finder.findPackages(searchPkg, false, false); // find matching subpackages
                                        res.addAll(finder.findClasses(lastPkg, var, false)); // matching classes
                                        result = new JavaResult(component, res, searchPkg + '*', item, 0);
                                    }
                                }
                            }
                        }
                        break;

                }
                break;

            case JCExpression.ARRAY:
                cont = resolveItem(item.getParameter(0), first, false);
                if (cont) {
                    cont = false;
                    if (lastType != null) { // must be type
                        if (item.getParameterCount() == 2) { // index in array follows
                            JCType arrayType = resolveType(item.getParameter(1));
                            if (arrayType != null && arrayType.equals(JavaCompletion.INT_TYPE)) {
                                lastType = JavaCompletion.getType(lastType.getClazz(),
                                                                  Math.max(lastType.getArrayDepth() - 1, 0));
                                cont = true;
                            }
                        } else { // no index, increase array depth
                            lastType = JavaCompletion.getType(lastType.getClazz(),
                                                              lastType.getArrayDepth() + 1);
                            cont = true;
                        }
                    }
                }
                break;

            case JCExpression.INSTANCEOF:
                lastType = JavaCompletion.BOOLEAN_TYPE;
                break;

            case JCExpression.OPERATOR:
                List res = new ArrayList();
                JCClass curCls = sup.getClass(item.getTokenOffset(0)); // 
                if (curCls != null) { //find all methods and fields for "this" class
                    res.addAll(findFieldsAndMethods(finder, curCls.getPackageName(), curCls, "", false,
                    sup.isStaticBlock(item.getTokenOffset(0)), true));
                }
                res.addAll(finder.findPackages("", false, false)); // find all packages
                res.addAll(finder.findClasses(null, "", false)); // find all classes
                
                result = new JavaResult(component, res, "*", item, endOffset, 0, 0); // NOI18N
                
                switch (item.getTokenID(0).getNumericID()) {
                    case JavaTokenContext.EQ_ID: // Assignment operators
                    case JavaTokenContext.PLUS_EQ_ID:
                    case JavaTokenContext.MINUS_EQ_ID:
                    case JavaTokenContext.MUL_EQ_ID:
                    case JavaTokenContext.DIV_EQ_ID:
                    case JavaTokenContext.AND_EQ_ID:
                    case JavaTokenContext.OR_EQ_ID:
                    case JavaTokenContext.XOR_EQ_ID:
                    case JavaTokenContext.MOD_EQ_ID:
                    case JavaTokenContext.LSHIFT_EQ_ID:
                    case JavaTokenContext.RSSHIFT_EQ_ID:
                    case JavaTokenContext.RUSHIFT_EQ_ID:
                        if (item.getParameterCount() > 0) {
                            lastType = resolveType(item.getParameter(0));
                            staticOnly = false;
                        }
                        break;

                    case JavaTokenContext.LT_ID: // Binary, result is boolean
                    case JavaTokenContext.GT_ID:
                    case JavaTokenContext.LT_EQ_ID:
                    case JavaTokenContext.GT_EQ_ID:
                    case JavaTokenContext.EQ_EQ_ID:
                    case JavaTokenContext.NOT_EQ_ID:
                    case JavaTokenContext.AND_AND_ID: // Binary, result is boolean
                    case JavaTokenContext.OR_OR_ID:
                        lastType = JavaCompletion.BOOLEAN_TYPE;
                        break;

                    case JavaTokenContext.LSHIFT_ID: // Always binary
                    case JavaTokenContext.RSSHIFT_ID:
                    case JavaTokenContext.RUSHIFT_ID:
                    case JavaTokenContext.MUL_ID:
                    case JavaTokenContext.DIV_ID:
                    case JavaTokenContext.AND_ID:
                    case JavaTokenContext.OR_ID:
                    case JavaTokenContext.XOR_ID:
                    case JavaTokenContext.MOD_ID:

                    case JavaTokenContext.PLUS_ID:
                    case JavaTokenContext.MINUS_ID:
                        switch (item.getParameterCount()) {
                        case 2:
                            JCType typ1 = resolveType(item.getParameter(0));
                            JCType typ2 = resolveType(item.getParameter(1));
                            if (typ1 != null && typ2 != null
                                    && typ1.getArrayDepth() == 0
                                    && typ2.getArrayDepth() == 0
                                    && JavaCompletion.isPrimitiveClass(typ1.getClazz())
                                    && JavaCompletion.isPrimitiveClass(typ2.getClazz())
                               ) {
                                lastType = sup.getCommonType(typ1, typ2);
                            }
                            break;
                        case 1: // get the only one parameter
                            JCType typ = resolveType(item.getParameter(0));
                            if (typ != null && JavaCompletion.isPrimitiveClass(typ.getClazz())) {
                                lastType = typ;
                            }
                            break;
                        }
                        break;

                    case JavaTokenContext.COLON_ID:
                        switch (item.getParameterCount()) {
                        case 2:
                            JCType typ1 = resolveType(item.getParameter(0));
                            JCType typ2 = resolveType(item.getParameter(1));
                            if (typ1 != null && typ2 != null) {
                                lastType = sup.getCommonType(typ1, typ2);
                            }
                            break;

                        case 1:
                            lastType = resolveType(item.getParameter(0));
                            break;
                        }
                        break;

                    case JavaTokenContext.QUESTION_ID:
                        if (item.getParameterCount() >= 2) {
                            lastType = resolveType(item.getParameter(1)); // should be colon
                        }
                        break;
                }
                break;

            case JCExpression.UNARY_OPERATOR:
                if (item.getParameterCount() > 0) {
                    lastType = resolveType(item.getParameter(0));
                }
                break;

            case JCExpression.CONVERSION:
                lastType = resolveType(item.getParameter(0));
                staticOnly = false;
                break;

            case JCExpression.TYPE:
                lastType = JavaCompletion.getPredefinedType(item.getType());
                break;

            case JCExpression.PARENTHESIS:
                cont = resolveItem(item.getParameter(0), first, last);
                break;

            case JCExpression.CONSTRUCTOR: // constructor can be part of a DOT expression
                isConstructor = true;
                cont = resolveExp(item.getParameter(0));
                staticOnly = false;
                break;

            case JCExpression.METHOD_OPEN: // Unclosed method
                methodOpen = true;
                // let it flow to method
            case JCExpression.METHOD: // Closed method
                String mtdName = item.getTokenText(0);

                // this() invoked, offer constructors
                if( ("this".equals(mtdName)) && (item.getTokenCount()>0) ){ //NOI18N
                    JCClass cls = sup.getClass(item.getTokenOffset(0));
                    if (cls != null) {
                        cls = finder.getExactClass(cls.getFullName());
                        if (cls != null) {
                            isConstructor = true;
                            mtdName = cls.getName();
                        }
                    }
                }
                
                // super() invoked, offer constructors for super class
                if( ("super".equals(mtdName)) && (item.getTokenCount()>0) ){ //NOI18N
                    JCClass cls = sup.getClass(item.getTokenOffset(0));
                    if (cls != null) {
                        cls = finder.getExactClass(cls.getFullName());
                        if (cls != null) {
                            cls = cls.getSuperclass();
                            if (cls != null) {
                                isConstructor = true;
                                mtdName = cls.getName();
                            }
                        }
                    }
                }
                
                if (isConstructor) { // Help for the constructor
                    JCClass cls = null;
                    if (first) {
                        cls = sup.getClassFromName(mtdName, true);
                    } else { // not first
                        if ((last)&&(lastPkg != null)) { // valid package
                            cls = JCUtilities.getExactClass(finder, mtdName, lastPkg.getName());
                        } else if (lastType != null) { 
                            if(last){ // inner class
                                cls = JCUtilities.getExactClass(finder, mtdName,
                                lastType.getClazz().getFullName());
                            }else{ 
                                if (lastType.getArrayDepth() == 0) { // Not array
                                    cls = lastType.getClazz();
                                } else { // Array of some depth
                                    cls = JavaCompletion.OBJECT_CLASS_ARRAY; // Use Object in this case
                                }
                            }
                        }
                    } 
                    if (cls != null) {
                        lastType = JavaCompletion.getType(cls, 0);
                        
                        List ctrList = (finder instanceof JCBaseFinder) ? 
                            JCUtilities.getConstructors(cls, ((JCBaseFinder)finder).showDeprecated()) :
                            JCUtilities.getConstructors(cls);
                        String parmStr = "*"; // NOI18N
                        List typeList = getTypeList(item);
                        List filtered = sup.filterMethods(ctrList, typeList, methodOpen);
                        if (filtered.size() > 0) {
                            ctrList = filtered;
                            parmStr = formatTypeList(typeList, methodOpen);
                        }
                        List mtdList = finder.findMethods(cls, mtdName, true, false, first);
                        if (mtdList.size() > 0) {
                            
                            if (last && !findType) {
                                result = new JavaResult(component, mtdList,
                                                        lastType.getClazz().getFullName() + '.' + mtdName + '(' + parmStr + ')',
                                                        item, endOffset, 0, 0);
                            } else {
                                    lastType = ((JCMethod)mtdList.get(0)).getReturnType();
                                    staticOnly = false;
                            }
                        } else{
                            result = new JavaResult(component, ctrList,
                            mtdName + '(' + parmStr + ')',
                            item, endOffset, 0, 0);
                        }
                    } else {
                        isConstructor = false;
                    }
                } 
                if (isConstructor == false){
                    // Help for the method
                    if (first) {
                        JCClass cls = sup.getClass(item.getTokenOffset(0));
                        if (cls != null) {
                            lastType = JavaCompletion.getType(cls, 0);
                        }
                    }

                    if (lastType != null) {
                        JCClass cls;
                        if (lastType.getArrayDepth() == 0) { // Not array
                            cls = lastType.getClazz();
                        } else { // Array of some depth
                            cls = JavaCompletion.OBJECT_CLASS_ARRAY; // Use Object in this case 
                        }
                        
                        List mtdList = finder.findMethods(cls, mtdName, true, false, first);
                        String parmStr = "*"; // NOI18N
                        List typeList = getTypeList(item);
                        List filtered = sup.filterMethods(mtdList, typeList, methodOpen);
                        if (filtered.size() > 0) {
                            mtdList = filtered;
                            parmStr = formatTypeList(typeList, methodOpen);
                        }
                        if (mtdList.size() > 0) {
                            if (last && !findType) {
                                result = new JavaResult(component, mtdList,
                                                        lastType.getClazz().getFullName() + '.' + mtdName + '(' + parmStr + ')',
                                                        item, endOffset, 0, 0);
                            } else {
                                if (mtdList.size() > 0) {
                                    lastType = ((JCMethod)mtdList.get(0)).getReturnType();
                                    staticOnly = false;
                                }
                            }
                        } else {
                            lastType = null; // no method found
                            cont = false;
                        }
                    } else { // package.method() is invalid
                        lastPkg = null;
                        cont = false;
                    }
                }
                break;
            }

            if (lastType == null && lastPkg == null) { // !!! shouldn't be necessary
                cont = false;
            }

            return cont;
        }

        private List getTypeList(JCExpression item) {
            int parmCnt = item.getParameterCount();
            ArrayList typeList = new ArrayList();
            if (parmCnt > 0) { // will try to filter by parameters
                boolean methodOpen = (item.getExpID() == JCExpression.METHOD_OPEN);
                for (int i = 0; i < parmCnt; i++) {
                    JCExpression parm = item.getParameter(i);
                    JCType typ = resolveType(parm);
                    typeList.add(typ);
                }
            }
            return typeList;
        }

    }

    private static String formatTypeList(List typeList, boolean methodOpen) {
        StringBuffer sb = new StringBuffer();
        if (typeList.size() > 0) {
            int cntM1 = typeList.size() - 1;
            for (int i = 0; i <= cntM1; i++) {
                JCType t = (JCType)typeList.get(i);
                if (t != null) {
                    sb.append(t.format(false));
                } else {
                    sb.append('?');
                }
                if (i < cntM1) {
                    sb.append(", "); // NOI18N
                }
            }
            if (methodOpen) {
                sb.append(", *"); // NOI18N
            }
        } else { // no parameters
            if (methodOpen) {
                sb.append("*"); // NOI18N
            }
        }
        return sb.toString();
    }

    public static class JavaResult extends CompletionQuery.DefaultResult {

        /** First offset in the name of the (inner) class
        * to be displayed. It's used to display the inner classes
        * of the main class to exclude the initial part of the name.
        */
        private int classDisplayOffset;

        /** Expression to substitute */
        private JCExpression substituteExp;

        /** Starting position of the text to substitute */
        private int substituteOffset;

        /** Length of the text to substitute */
        private int substituteLength;

        /** Component to update */
        private JTextComponent component;
        

        private List javaData = new ArrayList();
        
        public JavaResult(JTextComponent component, List data, String title,
                   JCExpression substituteExp, int classDisplayOffset) {
            this(component, data, title, substituteExp, substituteExp.getTokenOffset(0),
                 substituteExp.getTokenLength(0), classDisplayOffset);
        }

        public JavaResult(JTextComponent component, List data, String title,
                   JCExpression substituteExp, int substituteOffset,
                   int substituteLength, int classDisplayOffset) {
            super(component, title, convertData(data, classDisplayOffset, substituteExp), substituteOffset, substituteLength);
            
            this.component = component;
            this.substituteExp = substituteExp;
            this.substituteOffset = substituteOffset;
            this.substituteLength = substituteLength;
            this.classDisplayOffset = classDisplayOffset;
        }

        private static List convertData(List dataList, int classDisplayOffset, JCExpression substituteExp){
            Iterator iter = dataList.iterator();
            List ret = new ArrayList();
            while (iter.hasNext()){
                Object obj = iter.next();
                if (obj instanceof CompletionQuery.ResultItem){
                    ret.add(obj);
                }else{
                    ret.add(createResultItem(obj, classDisplayOffset, substituteExp));
                }
            }
            return ret;
        }
        
        private static CompletionQuery.ResultItem createResultItem(Object obj, int classDisplayOffset, JCExpression substituteExp){
            if (obj instanceof JCPackage) {
                return getJCItemFactory().createPackageResultItem((JCPackage)obj, false);
            } else if (obj instanceof JCClass) {
                return getJCItemFactory().createClassResultItem((JCClass)obj, classDisplayOffset, false);
            } else if (obj instanceof JCField) {
                return getJCItemFactory().createFieldResultItem((JCField)obj);
            } else if (obj instanceof JCMethod) {
                return getJCItemFactory().createMethodResultItem((JCMethod)obj, substituteExp);
            } else if (obj instanceof JCConstructor) {
                return getJCItemFactory().createConstructorResultItem((JCConstructor)obj, substituteExp);
            }
            return null;
        }
        

        protected JTextComponent getComponent(){
            return component;
        }
        
        protected int getSubstituteLength(){
            return substituteLength;
        }
        
        protected int getSubstituteOffset(){
            return substituteOffset;
        }
        
        protected JCExpression getSubstituteExp(){
            return substituteExp;
        }
        
        protected int getClassDisplayOffset(){
            return classDisplayOffset;
        }
        
        /** Get the text that is normally filled into the text if enter is pressed. */
        protected String getMainText(Object dataItem) {
            String text = null;
            if (dataItem instanceof JCPackage) {
                text = ((JCPackage)dataItem).getLastName();
            } else if (dataItem instanceof JCClass) {
                text = ((JCClass)dataItem).getName();
                if (classDisplayOffset > 0 && classDisplayOffset < text.length()) { // Only the last name for inner classes
                    text = text.substring(classDisplayOffset);
                }
            } else if (dataItem instanceof JCField) {
                text = ((JCField)dataItem).getName();
            } else if (dataItem instanceof JCMethod) {
                JCMethod mtd = (JCMethod)dataItem;
                text = mtd.getName();
            } else if (dataItem instanceof JCConstructor) {
                text = ((JCConstructor)dataItem).getClazz().getName();
            }
            return text;
        }

        /** Get the text that is common to all the entries in the query-result */
        protected String getCommonText(String prefix) {
            List data = getData();
            int cnt = data.size();
            int prefixLen = prefix.length();
            String commonText = null;
            for (int i = 0; i < cnt; i++) {
                String mainText = getMainText(data.get(i));
                if (mainText != null && mainText.startsWith(prefix)) {
                    mainText = mainText.substring(prefixLen);
                    if (commonText == null) {
                        commonText = mainText;
                    }
                    // Get largest common part
                    int minLen = Math.min(mainText.length(), commonText.length());
                    int commonInd;
                    for (commonInd = 0; commonInd < minLen; commonInd++) {
                        if (mainText.charAt(commonInd) != commonText.charAt(commonInd)) {
                            break;
                        }
                    }
                    if (commonInd != 0) {
                        commonText = commonText.substring(0, commonInd);
                    } else {
                        return null; // no common text
                    }
                }
            }
            return prefix + ((commonText != null) ? commonText : ""); // NOI18N
        }

        /** Update the text in response to pressing TAB key.
        * @return whether the text was successfully updated
        */
        public boolean substituteCommonText(int dataIndex) {
            
            List data = getData();
            if( data.size() == 0 ) return false;

            Object obj = getData().get( dataIndex );
            if (obj instanceof CompletionQuery.ResultItem){
                //return super.substituteCommonText(dataIndex); [PENDING] 
                // how to get getCommonText to CompletionQuery.ResultItem ???
            }
            
            BaseDocument doc = (BaseDocument)component.getDocument();
            try {
                String prefix = doc.getText(substituteOffset, substituteLength);
                String commonText = getCommonText(prefix);
                if (commonText != null) {
                    if(substituteExp!=null){
                        if( (substituteExp.getExpID()==JCExpression.METHOD_OPEN) || (substituteExp.getExpID()==JCExpression.METHOD) ) 
                            return true;
                    }
                    doc.atomicLock();
                    try {
                        doc.remove(substituteOffset, substituteLength);
                        doc.insertString(substituteOffset, commonText, null);
                    } finally {
                        doc.atomicUnlock();
                    }
                }
            } catch (BadLocationException e) {
                // no updating
            }
            return true;
        }

        /** Update the text in response to pressing ENTER.
        * @return whether the text was successfully updated
        */
        public boolean substituteText(int dataIndex, boolean shift ) {
            Object actData = getData().get( dataIndex );
            if (actData instanceof CompletionQuery.ResultItem){
                return super.substituteText(dataIndex, shift);
            }
            
            // the rest part of code is here only for backward compatibility...
            // it should be removed later if all data will be CompletionQuery.ResultItem
            

            BaseDocument doc = (BaseDocument)component.getDocument();
            String text = null;
            int selectionStartOffset = -1;
            int selectionEndOffset = -1;
            Object replacement = getData().get(dataIndex);

            if (replacement instanceof JCPackage) {
                text = ((JCPackage)replacement).getLastName();

            } else if (replacement instanceof JCClass) {
                text = ((JCClass)replacement).getName();
                if (classDisplayOffset > 0
                        && classDisplayOffset < text.length()
                   ) { // Only the last name for inner classes
                    text = text.substring(classDisplayOffset);
                }

            } else if (replacement instanceof JCField) {
                text = ((JCField)replacement).getName();

            } else if (replacement instanceof JCConstructor) {
                JCConstructor mtd = (JCConstructor)replacement;
                switch ((substituteExp != null) ? substituteExp.getExpID() : -1) {
                case JCExpression.METHOD:
                    // no substitution
                    break;

                case JCExpression.METHOD_OPEN:
                    JCParameter[] parms = mtd.getParameters();
                    if (parms.length == 0) {
                        text = ")"; // NOI18N
                    } else { // one or more parameters
                        int ind = substituteExp.getParameterCount();
                        boolean addSpace = false;
                        Formatter f = doc.getFormatter();
                        if (f instanceof ExtFormatter) {
                            Object o = ((ExtFormatter)f).getSettingValue(JavaSettingsNames.JAVA_FORMAT_SPACE_AFTER_COMMA);
                            if ((o instanceof Boolean) && ((Boolean)o).booleanValue()) {
                                addSpace = true;
                            }
                        }

                        try {
                            if (addSpace && (ind == 0 || (substituteOffset > 0
                                                          && Character.isWhitespace(doc.getText(substituteOffset - 1, 1).charAt(0))))
                               ) {
                                addSpace = false;
                            }
                        } catch (BadLocationException e) {
                        }

                        if (ind < parms.length) {
                            text = addSpace ? " " : ""; // NOI18N
                            selectionStartOffset = text.length();
                            text += parms[ind].getName();
                            selectionEndOffset = text.length();
                        }
                    }
                    break;

                default:
                    text = getMainText(replacement);
                    boolean addSpace = false;
                    Formatter f = doc.getFormatter();
                    if (f instanceof ExtFormatter) {
                        Object o = ((ExtFormatter)f).getSettingValue(JavaSettingsNames.JAVA_FORMAT_SPACE_BEFORE_PARENTHESIS);
                        if ((o instanceof Boolean) && ((Boolean)o).booleanValue()) {
                            addSpace = true;
                        }
                    }

                    if (addSpace) {
                        text += ' ';
                    }
                    text += '(';

                    parms = mtd.getParameters();
                    if (parms.length > 0) {
                        selectionStartOffset = text.length();
                        text += parms[0].getName();
                        selectionEndOffset = text.length();
                    } else {
                        text += ")"; // NOI18N
                    }
                    break;
                }

            } else if (replacement instanceof JCConstructor) {
                text = ((JCConstructor)replacement).getClazz().getName();
            }

            if (text != null) {
                // Update the text
                doc.atomicLock();
                try {
                    // bugfix of #41492
                    String textToReplace = doc.getText(substituteOffset, substituteLength);
                    if (text.equals(textToReplace)) return false;
                    doc.remove(substituteOffset, substituteLength);
                    doc.insertString(substituteOffset, text, null);
                    if (selectionStartOffset >= 0) {
                        component.select(substituteOffset + selectionStartOffset,
                                         substituteOffset + selectionEndOffset);
                    }
                } catch (BadLocationException e) {
                    // Can't update
                } finally {
                    doc.atomicUnlock();
                }
            }

            return true;
             
        }

    }


    protected void setJCItemFactory(JCItemFactory itemFactory){
        this.itemFactory = itemFactory;
    }
    
    public static JCItemFactory getJCItemFactory(){
        return itemFactory;
    }
 
    public interface JCItemFactory{
        public JCResultItem.PackageResultItem createPackageResultItem(JCPackage pkg, boolean displayFullPackagePath);
        public JCResultItem.ClassResultItem createClassResultItem(JCClass cls, int classDisplayOffset, boolean displayFQN);
        public JCResultItem.FieldResultItem createFieldResultItem(JCField fld);
        public JCResultItem.MethodResultItem createMethodResultItem(JCMethod mtd, JCExpression substituteExp);
        public JCResultItem.ConstructorResultItem createConstructorResultItem(JCConstructor ctr, JCExpression substituteExp);
    }
    
    public static class DefaultJCItemFactory implements JCItemFactory{
        public DefaultJCItemFactory(){
        }

        public JCResultItem.PackageResultItem createPackageResultItem(JCPackage pkg, boolean displayFullPackagePath){
            return new JCResultItem.PackageResultItem(pkg, displayFullPackagePath);
        }
        public JCResultItem.ClassResultItem createClassResultItem(JCClass cls, int classDisplayOffset, boolean displayFQN){
            return new JCResultItem.ClassResultItem(cls, classDisplayOffset, displayFQN);
        }
        public JCResultItem.FieldResultItem createFieldResultItem(JCField fld){
            return new JCResultItem.FieldResultItem(fld);
        }
        public JCResultItem.MethodResultItem createMethodResultItem(JCMethod mtd, JCExpression substituteExp){
            return new JCResultItem.MethodResultItem(mtd, substituteExp);
        }
        public JCResultItem.ConstructorResultItem createConstructorResultItem(JCConstructor ctr, JCExpression substituteExp){
            return new JCResultItem.ConstructorResultItem(ctr, substituteExp);
        }
        
    }


}
... 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.