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.modules.editor.java;

import java.awt.Cursor;
import java.awt.Dialog;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.util.*;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import org.netbeans.api.mdr.MDRepository;
import org.netbeans.editor.BaseAction;
import org.netbeans.editor.BaseDocument;
import org.netbeans.jmi.javamodel.*;
import org.netbeans.jmi.javamodel.JavaPackage;
import org.netbeans.modules.javacore.ClassIndex;
import org.netbeans.modules.javacore.internalapi.JavaMetamodel;
import org.openide.DialogDescriptor;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.awt.StatusDisplayer;
import org.openide.filesystems.FileObject;
import org.openide.loaders.DataObject;
import org.openide.util.NbBundle;
import org.openide.windows.WindowManager;

public class JavaFixAllImports extends BaseAction {
    static final long serialVersionUID = 6020950800832542269L;
    
    private static final int HAS_SYNTAX_ERROR = 16; // [PENDING]
    
    private Cursor editCursor, mainCursor;
    private Frame mainWindow;
    private JMIUtils jmiUtils;

    public JavaFixAllImports() {
        super(JavaKit.fixImportsAction);
        putValue(SHORT_DESCRIPTION, NbBundle.getBundle(JavaKit.class).getString("fix-imports"));
        putValue("helpID", JavaFixAllImports.class.getName()); // NOI18N
    }
    
    public void actionPerformed(ActionEvent evt, JTextComponent target) {
        if (target == null)
            return;
        
        addAllNeededImports(target.getText(), (BaseDocument)target.getDocument(), target);
    }
    
    public void addAllNeededImports(String txt, BaseDocument doc, JTextComponent target) {
        Object sdp = doc.getProperty(Document.StreamDescriptionProperty);
        FileObject fo = null;
        if (sdp instanceof FileObject) {
            fo = (FileObject)sdp;
        } else if (sdp instanceof DataObject) {
            fo = ((DataObject)sdp).getPrimaryFile();
        } else {
            return;
        }
        
        editCursor = target.getCursor();
        mainWindow = WindowManager.getDefault().getMainWindow();
        mainCursor = mainWindow.getCursor();
        Cursor waitCursor = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);
        target.setCursor(waitCursor);
        mainWindow.setCursor(waitCursor);
        
        ArrayList importsToAdd = null, importsToRemove = null;
        HashSet unresolved = null;
        boolean failed = true;
        boolean diagPassed = true;
        boolean hasSyntaxError;
        jmiUtils = JMIUtils.get(doc);
        jmiUtils.beginTrans(true);
        try {
            Resource resource = JavaMetamodel.getManager().getResource(fo);
            hasSyntaxError = (resource.getStatus() & HAS_SYNTAX_ERROR) > 0;

            if (!hasSyntaxError) {
                HashSet clsNames = new HashSet();
                HashSet resolvedClsNames = new HashSet();
                findPotentialClassNames(resource, clsNames, resolvedClsNames);
                HashMap importedPackages = new HashMap();
                HashMap importedClasses = new HashMap();

                for (Iterator iter = resource.getImports().iterator(); iter.hasNext();) {
                    Import imp = (Import) iter.next();
                    if (imp.isStatic())
                        continue;
                    String id = idToName(imp.getIdentifier());
                    if (imp.getImportedElement() instanceof JavaPackage) {
                        importedPackages.put(id, imp);
                    } else {
                        if (imp.isOnDemand()) {
                            id = id + ".*"; // NOI18N
                        }
                        importedClasses.put(id, imp);
                    }
                }

                importsToAdd = new ArrayList();
                importsToRemove = new ArrayList();
                unresolved = new HashSet();
            
                diagPassed = calculateAndAddImports(clsNames, resolvedClsNames, importedPackages, importedClasses, resource, importsToAdd, importsToRemove, unresolved, target);
            }
            
            failed = false;
        } finally {
            target.setCursor(editCursor);
            mainWindow.setCursor(mainCursor);
            jmiUtils.endTrans(failed);
        }

        if (hasSyntaxError) {
            String msg = NbBundle.getMessage(JavaFixAllImports.class, "SourceContainsSyntaxErrors_Lbl");
            StatusDisplayer.getDefault().setStatusText(msg);
        } else if (unresolved.size() > 0) {
            showInformational(unresolved);
        } else if (diagPassed) {
            String msg = null;
            if (importsToAdd.size() > 0 || importsToRemove.size() > 0) {
                msg = NbBundle.getMessage(JavaFixAllImports.class, "AllImportsWereFixed_Lbl");
            } else {
                msg = NbBundle.getMessage(JavaFixAllImports.class, "NoImportsToFix_Lbl");
            }
            StatusDisplayer.getDefault().setStatusText(msg);
        }
    }
    
    boolean calculateAndAddImports(HashSet names, HashSet resolvedNames,
        HashMap importedPackages, HashMap importedClasses, Resource resource,
        ArrayList importsToAdd, List importsToRemove, HashSet unresolved, JTextComponent target) {
                
        HashMap ambigs = new HashMap();
        HashSet usedImports = new HashSet();        
        Import imp;
        FileObject[] cpRoots = JavaMetamodel.getManager().getClassPath().getRoots();
        ClassIndex[] cis = new ClassIndex[cpRoots.length];
        for (int i = 0; i < cpRoots.length; i++) {
            cis[i] = ClassIndex.getIndex(JavaMetamodel.getManager().getJavaExtent(cpRoots[i]));
        }

        // detect imports corresponding to resolved types
        for (Iterator iter = resolvedNames.iterator(); iter.hasNext();) {
            JavaClass jc = (JavaClass)iter.next();
            imp = (Import)importedClasses.get(jc.getName());
            if (imp != null) {
                usedImports.add(imp);
            } else {
                Object comp = jc.refImmediateComposite();
                if (comp instanceof Resource) {
                    imp = (Import)importedPackages.get(((Resource)comp).getPackageName());
                    if (imp != null) {
                        usedImports.add(imp);
                    }
                } else if (comp instanceof JavaClass) {
                    imp = (Import)importedClasses.get(((JavaClass)comp).getName() + ".*"); // NOI18N
                    if (imp != null) {
                        usedImports.add(imp);
                    }
                }
            }
        } // for

        Iterator classifiers = resource.getClassifiers().iterator();
        JavaClass from = classifiers.hasNext() ? (JavaClass)classifiers.next() : null;

        Outer:
            for (Iterator iter = names.iterator(); iter.hasNext();) {
                String simpleName = (String)iter.next();

                Collection ret = new ArrayList();
                for (int i = 0; i < cis.length; i++) {
                    if (cis[i] == null)
                        continue;
                    Collection res = cis[i].getClassesBySimpleName(simpleName);
                    if (res != null) {
                        for (Iterator tmpIt = res.iterator(); tmpIt.hasNext();) {
                            JavaClass javaClass = (JavaClass) tmpIt.next();
                            if (jmiUtils.isAccessible(javaClass, from))
                                ret.add(javaClass);
                        }
                    }
                }
                if (ret.size() == 0) {
                    unresolved.add(simpleName);
                    continue;
                }

                for (Iterator iter2 = ret.iterator(); iter2.hasNext();) {
                    JavaClass cls = (JavaClass)iter2.next();
                    Object comp = cls.refImmediateComposite();
                    String pkg = null;
                    if (comp instanceof Resource) {
                        pkg = ((Resource)comp).getPackageName();
                    }
                    if (pkg != null) {
                        if (pkg.equals("java.lang")) { // NOI18N
                            continue Outer;
                        }
                        imp = (Import) importedPackages.get(pkg);
                        if (imp != null) { // NOI18N
                            usedImports.add(imp);
                            continue Outer;
                        }
                    }

                    imp = (Import) importedClasses.get(cls.getName());
                    if (imp != null) {
                        usedImports.add(imp);
                        continue Outer;
                    }
                    if (comp instanceof JavaClass) {
                        imp = (Import)importedClasses.get(((JavaClass)comp).getName() + ".*"); // NOI18N
                        if (imp != null) {
                            usedImports.add(imp);
                            continue Outer;
                        }
                    }
                }

                int sz = ret.size();
                if (sz > 1)
                    ambigs.put(simpleName, ret);
                else if (sz > 0)
                    importsToAdd.add(((JavaClass)ret.iterator().next()).getName());
            }
            
            target.setCursor(editCursor);
            mainWindow.setCursor(mainCursor);            
            
            String[] selections = null;
            boolean ambigsWasCalled = false;
            if (ambigs != null && ambigs.size() > 0) {
                selections = userResolvesAmbiguities(ambigs);
                ambigsWasCalled = true;
            }
            
            if (ambigsWasCalled) {
                if (selections != null) {
                    for (int i = 0; i < selections.length; i++)
                        importsToAdd.add(selections[i]);
                } else {
                    // user Cancelled the "Add Imports" Action
                    return false;
                }
            }
            
            changeImports(importsToAdd, importsToRemove, usedImports, resource);
            return true;
    }
    
    // Present to the user all the ambiguous imports and get their repsonse back
    private static String[] userResolvesAmbiguities(HashMap ambigs) {
        int numberOfAmbigs = ambigs.size();
        
        // For now, we simply use the first element as the default. We could be smarter in the future
        int cnt = 0;
        String[] simpleNames = new String[numberOfAmbigs];
        String[][] choices = new String[numberOfAmbigs][];
        String[] defaults = new String[numberOfAmbigs];
        int [] defaultWeights = new int[numberOfAmbigs];

        for (Iterator ambigIter = ambigs.keySet().iterator(); ambigIter.hasNext(); cnt++) {
            String simpleName = (String)ambigIter.next();
            List l = (List)ambigs.get(simpleName);
            
            simpleNames[cnt] = simpleName;
            String[] curChoice = choices[cnt] = new String[l.size()];
            defaultWeights[cnt] = -1;

            for (int k = 0; k < l.size(); k++) {
                JavaClass cls = (JavaClass)l.get(k);
                String fullName = cls.getName();
                curChoice[k] = fullName;
                int weight = JMIUtils.getDefaultSelectionWeight(cls);
                if (weight > defaultWeights[cnt]) {
                    defaults[cnt] = fullName;
                    defaultWeights[cnt] = weight;
                }
            }
            Arrays.sort(curChoice);
        }
        
        FixDuplicateImportStmts pp = new FixDuplicateImportStmts(simpleNames, choices, defaults);
        DialogDescriptor dd = new DialogDescriptor(pp,
        NbBundle.getMessage(JavaFixAllImports.class, "FixDuplicateImports_dlgTitle"));
        dd.setOptionType(DialogDescriptor.OK_CANCEL_OPTION);
        
        final Dialog d  = org.openide.DialogDisplayer.getDefault().createDialog(dd);
        d.pack();
        d.setVisible(true);
        Object o = dd.getValue();
        if (o == DialogDescriptor.OK_OPTION) {
            return pp.getSelections();
        } else { // Cancel pressed
            d.setVisible(false);
            d.dispose();
            return null;
        }
    }

    private static void showInformational(HashSet unresolved) {
        StringBuffer sb = new StringBuffer(NbBundle.getMessage(JavaFixAllImports.class, "NoImportsFound_Lbl"));
        sb.append('\n');
        for (Iterator iter = unresolved.iterator(); iter.hasNext();) {
            sb.append("     - ");    // NOI18N
            sb.append((String)iter.next());
            sb.append('\n');    // NOI18N
        }
        
        NotifyDescriptor d = new NotifyDescriptor.Message(sb.toString());
        DialogDisplayer.getDefault().notify(d);
    }
    
    private static void changeImports(ArrayList importsToAdd, List importsToRemove, Set usedImports, Resource resource) {
        ListIterator iter;
        Object[] importsArray = importsToAdd.toArray();
        Arrays.sort(importsArray,
        new Comparator() {
            public int compare(Object o1, Object o2) {
                return ((String)o1).compareTo((String)o2);
            }
        });
        String thisPkgName = resource.getPackageName();
        List imports = resource.getImports();
        for (iter = imports.listIterator(); iter.hasNext();) {
            Import imp = (Import)iter.next();
            if (imp.isStatic())
                continue;
            if (!usedImports.contains(imp)) {
                importsToRemove.add(imp);
                iter.remove();
            } else {
                String pkgName = null;
                if (!imp.isOnDemand()) {
                    JavaClass jc = (JavaClass) imp.getImportedElement();
                    pkgName = jc.getResource().getPackageName();
                } else {
                    Object obj = imp.getImportedElement();
                    if (obj instanceof JavaPackage) {
                        pkgName = ((JavaPackage) obj).getName();
                    }
                }
                if (pkgName != null && (pkgName.equals("java.lang") || pkgName.equals(thisPkgName))) {
                    importsToRemove.add(imp);
                    iter.remove();
                }
            }
        }
        
        if (importsArray.length == 0)
            return;
        ImportClass proxy = ((JavaModelPackage)resource.refOutermostPackage()).getImport();
        Import imp = null;
        iter = imports.listIterator(imports.size());
        
        String currName = null;
        if (iter.hasPrevious()) {
            currName = ((Import)iter.previous()).getName();
            iter.next();
        }
        for (int i = importsArray.length - 1; i >=0; i--) {
            String impName = (String)importsArray[i];
            imp = proxy.createImport(impName, null, false, false);
            while (currName != null && (currName.compareTo(impName) > 0)) {
                iter.previous();
                if (iter.hasPrevious()) {
                    currName = ((Import)iter.previous()).getName();
                    iter.next();
                } else {
                    currName = null;
                }
            }
            iter.add(imp);
            iter.previous();
        }
    }
    
    // ...........................................................................
    
    private static void idToName(StringBuffer buffer, MultipartId id) {
        MultipartId parent = id.getParent();
        if (parent != null) {
            idToName(buffer, parent);
            buffer.append('.');
        }
        buffer.append(id.getName());
    }
    
    private static String idToName(MultipartId id) {
        StringBuffer buffer = new StringBuffer();
        idToName(buffer, id);
        return buffer.toString();
    }
    
    private static void findPotentialClassNames(Set set, Set resolved, Element elem, Set checkedElements) {
        Iterator iterator;
        if (elem instanceof ArrayReference) {
            elem = ((ArrayReference)elem).getParent();
        }
        if (elem instanceof MultipartId) {
            List typeArgs = new ArrayList();
            MultipartId id = (MultipartId)elem;
            typeArgs.addAll(id.getTypeArguments());
            while (id.getParent() != null) {
                id = id.getParent();
                typeArgs.addAll(id.getTypeArguments());
            }
            NamedElement namedElem = id.getElement();
            if (namedElem instanceof UnresolvedClass) {
                set.add(idToName(id));
            } else if (namedElem instanceof JavaClass) {
                resolved.add(namedElem);
            }
            iterator = typeArgs.iterator();
        } else {
            iterator = elem.getChildren().iterator();
        }
        for (; iterator.hasNext();) {
            Element usedElem = (Element)iterator.next();
            if (!checkedElements.contains(usedElem)) {
                checkedElements.add(usedElem);
                findPotentialClassNames(set, resolved, usedElem, checkedElements);
            }
        }
    }
    
    private static void findPotentialClassNames(Resource resource, Set unresolved, Set resolved) {
        HashSet checkedElements = new HashSet();
        for (Iterator iter = resource.getClassifiers().iterator(); iter.hasNext();) {
            findPotentialClassNames(unresolved, resolved, (Element)iter.next(), checkedElements);
        }
    }
    
}
... 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.