diff --git a/java.sourceui/src/org/netbeans/modules/java/source/ui/JavaSymbolDescriptor.java b/java.sourceui/src/org/netbeans/modules/java/source/ui/AsyncJavaSymbolDescriptor.java copy from java.sourceui/src/org/netbeans/modules/java/source/ui/JavaSymbolDescriptor.java copy to java.sourceui/src/org/netbeans/modules/java/source/ui/AsyncJavaSymbolDescriptor.java --- a/java.sourceui/src/org/netbeans/modules/java/source/ui/JavaSymbolDescriptor.java +++ b/java.sourceui/src/org/netbeans/modules/java/source/ui/AsyncJavaSymbolDescriptor.java @@ -44,93 +44,118 @@ import java.io.File; import java.io.IOException; -import java.net.URL; import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; +import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.swing.Icon; +import javax.swing.event.ChangeListener; import org.netbeans.api.annotations.common.CheckForNull; import org.netbeans.api.annotations.common.NonNull; import org.netbeans.api.annotations.common.NullAllowed; import org.netbeans.api.java.classpath.ClassPath; import org.netbeans.api.java.source.ClasspathInfo; +import org.netbeans.api.java.source.CompilationController; import org.netbeans.api.java.source.ElementHandle; +import org.netbeans.api.java.source.JavaSource; import org.netbeans.api.java.source.SourceUtils; +import org.netbeans.api.java.source.Task; import org.netbeans.api.java.source.ui.ElementOpen; import org.netbeans.api.project.Project; import org.netbeans.api.project.ProjectInformation; -import org.netbeans.api.project.ProjectUtils; +import org.netbeans.modules.java.source.indexing.TransactionContext; +import org.netbeans.modules.java.source.parsing.FileManagerTransaction; import org.netbeans.modules.java.source.parsing.FileObjects; +import org.netbeans.modules.java.source.parsing.ProcessorGenerated; import org.netbeans.modules.java.source.usages.ClassIndexImpl; +import org.netbeans.modules.java.source.usages.ClasspathInfoAccessor; import org.netbeans.modules.java.ui.Icons; import org.netbeans.spi.java.classpath.support.ClassPathSupport; +import org.netbeans.spi.jumpto.support.AsyncDescriptor; import org.netbeans.spi.jumpto.symbol.SymbolDescriptor; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; +import org.openide.util.ChangeSupport; import org.openide.util.Exceptions; +import org.openide.util.Pair; +import org.openide.util.RequestProcessor; /** * * @author Tomas Zezula */ -public class JavaSymbolDescriptor extends SymbolDescriptor { +public class AsyncJavaSymbolDescriptor extends SymbolDescriptor implements AsyncDescriptor { - private final String simpleName; - private final String simpleNameSuffix; + private static final RequestProcessor WORKER = new RequestProcessor(AsyncJavaSymbolDescriptor.class); + private static final String INIT = ""; //NOI18N + private final ElementHandle owner; - private final ElementHandle me; - private final ElementKind kind; - private final Set modifiers; private final FileObject root; private final Project project; private final ClassIndexImpl ci; + private final String ident; + private final ChangeSupport changeSupport; + private FileObject cachedFo; private volatile String cachedPath; - public JavaSymbolDescriptor ( - @NonNull final String simpleName, - @NullAllowed final String simpleNameSuffix, - @NonNull final ElementKind kind, - @NonNull final Set modifiers, - @NonNull final ElementHandle owner, - @NonNull final ElementHandle me, + private final AtomicReference state; + private String simpleName; + private String simpleNameSuffix; + private ElementHandle me; + private ElementKind kind; + private Set modifiers; + + public AsyncJavaSymbolDescriptor ( @NullAllowed final Project project, @NonNull final FileObject root, - @NonNull final ClassIndexImpl ci) { - assert simpleName != null; - assert kind != null; - assert modifiers != null; + @NonNull final ClassIndexImpl ci, + @NonNull final ElementHandle owner, + @NonNull final String ident) { assert owner != null; - assert me != null; assert root != null; assert ci != null; - this.simpleName = simpleName; - this.simpleNameSuffix = simpleNameSuffix; - this.kind = kind; - this.modifiers = modifiers; + assert ident != null; this.owner = owner; - this.me = me; this.root = root; this.project = project; this.ci = ci; + this.ident = ident; + this.changeSupport = new ChangeSupport(this); + this.state = new AtomicReference<>(State.NEW); } @Override public Icon getIcon() { - return Icons.getElementIcon(kind, modifiers); + if (State.INITIALIZED == this.state.get()) { + return Icons.getElementIcon(kind, modifiers); + } else { + initialize(false); + return null; + } } @Override public String getSymbolName() { - return simpleNameSuffix == null ? - simpleName : - simpleName + simpleNameSuffix; + if (State.INITIALIZED == this.state.get()) { + return simpleNameSuffix == null ? + simpleName : + simpleName + simpleNameSuffix; + } else { + initialize(false); + return ident; + } } @Override public String getSimpleName() { - return simpleName; + if (State.INITIALIZED == this.state.get()) { + return simpleName; + } else { + return ident; + } } @Override @@ -194,7 +219,9 @@ FileObject file = getFileObject(); if (file != null) { ClasspathInfo cpInfo = ClasspathInfo.create(file); - + if (State.INITIALIZED != this.state.get()) { + initialize(true); + } ElementOpen.open(cpInfo, me); } } @@ -221,16 +248,6 @@ return -1; } - @NonNull - public ElementKind getElementKind() { - return kind; - } - - @NonNull - public Set getModifiers() { - return modifiers; - } - @CheckForNull private ProjectInformation getProjectInfo() { return project == null ? @@ -238,4 +255,94 @@ project.getLookup().lookup(ProjectInformation.class); //Intentionally does not use ProjectUtils.getInformation() it does project icon annotation which is expensive } + private void initialize(final boolean force) { + if (force || state.compareAndSet(State.NEW, State.INITIALIZING)) { + final Runnable action = new Resolver(); + if (force) { + action.run(); + } else { + WORKER.execute(action); + } + } + } + + @Override + public void addChangeListener(ChangeListener listener) { + changeSupport.addChangeListener(listener); + } + + @Override + public void removeChangeListener(ChangeListener listener) { + changeSupport.removeChangeListener(listener); + } + + private final class Resolver implements Runnable { + + @Override + public void run() { + try { + TransactionContext. + beginTrans(). + register(FileManagerTransaction.class, FileManagerTransaction.read()). + register(ProcessorGenerated.class, ProcessorGenerated.nullWrite()); + try { + final ClasspathInfo cpInfo = ClasspathInfoAccessor.getINSTANCE().create(root,null,true,true,false,false); + final JavaSource js = JavaSource.create(cpInfo); + js.runUserActionTask(new Task() { + @Override + public void run (final CompilationController controller) { + final TypeElement te = owner.resolve(controller); + if (te != null) { + if (ident.equals(getSimpleName(te, null)) /*TODO && matchesRestrictions(te, restriction)*/) { + simpleName = te.getSimpleName().toString(); + simpleNameSuffix = null; + kind = te.getKind(); + modifiers = te.getModifiers(); + me = ElementHandle.create(te); + state.set(State.INITIALIZED); + changeSupport.fireChange(); + } + for (Element ne : te.getEnclosedElements()) { + if (ident.equals(getSimpleName(ne, te)) /*TODO && matchesRestrictions(ne, restriction)*/) { + final Pair name = JavaSymbolProvider.getDisplayName(ne, te); + simpleName = name.first(); + simpleNameSuffix = name.second(); + kind = ne.getKind(); + modifiers = ne.getModifiers(); + me = ElementHandle.create(ne); + state.set(State.INITIALIZED); + changeSupport.fireChange(); + break; + } + } + } + } + }, true); + }finally { + TransactionContext.get().commit(); + } + } catch (IOException e) { + Exceptions.printStackTrace(e); + } + } + + private String getSimpleName ( + @NonNull final Element element, + @NullAllowed final Element enclosingElement) { + String result = element.getSimpleName().toString(); + if (enclosingElement != null && INIT.equals(result)) { + result = enclosingElement.getSimpleName().toString(); + } +//TODO if (!caseSensitive) { + result = result.toLowerCase(); +// } + return result; + } + } + + private enum State { + NEW, + INITIALIZING, + INITIALIZED + } } diff --git a/java.sourceui/src/org/netbeans/modules/java/source/ui/JavaSymbolProvider.java b/java.sourceui/src/org/netbeans/modules/java/source/ui/JavaSymbolProvider.java --- a/java.sourceui/src/org/netbeans/modules/java/source/ui/JavaSymbolProvider.java +++ b/java.sourceui/src/org/netbeans/modules/java/source/ui/JavaSymbolProvider.java @@ -230,68 +230,11 @@ final Map,Set> r = new HashMap<>(); impl.getDeclaredElements(ident, kind, DocumentUtil.elementHandleConvertor(),r); if (!r.isEmpty()) { - //Needs FileManagerTransaction as it creates CPI with backgroundCompilation == true - TransactionContext. - beginTrans(). - register(FileManagerTransaction.class, FileManagerTransaction.read()). - register(ProcessorGenerated.class, ProcessorGenerated.nullWrite()); - try { - final ClasspathInfo cpInfo = ClasspathInfoAccessor.getINSTANCE().create(root,null,true,true,false,false); - final JavaSource js = JavaSource.create(cpInfo); - js.runUserActionTask(new Task() { - @Override - public void run (final CompilationController controller) { - for (final Map.Entry,Set> p : r.entrySet()) { - final ElementHandle owner = p.getKey(); - final TypeElement te = owner.resolve(controller); - final Set idents = p.getValue(); - if (te != null) { - if (idents.contains(getSimpleName(te, null)) && matchesRestrictions(te, restriction)) { - result.addResult(new JavaSymbolDescriptor( - te.getSimpleName().toString(), - null, - te.getKind(), - te.getModifiers(), - owner, - ElementHandle.create(te), - project, - root, - impl)); - } - for (Element ne : te.getEnclosedElements()) { - if (idents.contains(getSimpleName(ne, te)) && matchesRestrictions(ne, restriction)) { - final Pair name = getDisplayName(ne, te); - result.addResult(new JavaSymbolDescriptor( - name.first(), - name.second(), - ne.getKind(), - ne.getModifiers(), - owner, - ElementHandle.create(ne), - project, - root, - impl)); - } - } - } - } - } - - private String getSimpleName ( - @NonNull final Element element, - @NullAllowed final Element enclosingElement) { - String result = element.getSimpleName().toString(); - if (enclosingElement != null && INIT.equals(result)) { - result = enclosingElement.getSimpleName().toString(); - } - if (!caseSensitive) { - result = result.toLowerCase(); - } - return result; - } - },true); - } finally { - TransactionContext.get().commit(); + for (final Map.Entry,Set> p : r.entrySet()) { + final ElementHandle owner = p.getKey(); + for (String symbol : p.getValue()) { + result.addResult(new AsyncJavaSymbolDescriptor(project, root, impl, owner, symbol)); + } } } @@ -352,7 +295,7 @@ } @NonNull - private static Pair getDisplayName ( + static Pair getDisplayName ( @NonNull final Element e, @NonNull final Element enclosingElement) { assert e != null; diff --git a/jumpto/src/org/netbeans/modules/jumpto/symbol/ContentProviderImpl.java b/jumpto/src/org/netbeans/modules/jumpto/symbol/ContentProviderImpl.java --- a/jumpto/src/org/netbeans/modules/jumpto/symbol/ContentProviderImpl.java +++ b/jumpto/src/org/netbeans/modules/jumpto/symbol/ContentProviderImpl.java @@ -48,7 +48,6 @@ import java.util.Collection; import java.util.Collections; import java.util.IdentityHashMap; -import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.concurrent.Callable; @@ -65,12 +64,13 @@ import javax.swing.SwingUtilities; import org.netbeans.api.annotations.common.CheckForNull; import org.netbeans.api.annotations.common.NonNull; -import org.netbeans.api.annotations.common.NullAllowed; import org.netbeans.modules.jumpto.common.AbstractModelFilter; import org.netbeans.modules.jumpto.common.CurrentSearch; +import org.netbeans.modules.jumpto.common.Factory; import org.netbeans.modules.jumpto.common.ItemRenderer; import org.netbeans.modules.jumpto.common.Models; import org.netbeans.modules.jumpto.common.Utils; +import org.netbeans.spi.jumpto.support.AsyncDescriptor; import org.netbeans.spi.jumpto.symbol.SymbolDescriptor; import org.netbeans.spi.jumpto.symbol.SymbolProvider; import org.netbeans.spi.jumpto.type.SearchType; @@ -272,7 +272,8 @@ @Override public String getHighlightText(@NonNull final SymbolDescriptor item) { - return SymbolProviderAccessor.DEFAULT.getHighlightText(item); + return SymbolProviderAccessor.DEFAULT.getHighlightText( + RefreshableSymbolDescriptor.unwrap(item)); } @Override @@ -362,9 +363,12 @@ lastSize = newSize[0]; lastProvCount = newProvCount; Collections.sort(mergedSymbols, new SymbolComparator()); - final ListModel fmodel = Models.fromList( - mergedSymbols, - currentSearch.resetFilter()); + final ListModel fmodel = Models.refreshable( + Models.fromList( + mergedSymbols, + currentSearch.resetFilter()), + new AsyncConvertor() + ); if ( isCanceled ) { LOG.log( Level.FINE, @@ -498,7 +502,7 @@ } } - private static class Result { + private static final class Result { final List symbols; final int retry; final Collection nonFinishedProviders; @@ -516,4 +520,14 @@ this.nonFinishedProviders.isEmpty(); } } + + private static final class AsyncConvertor implements Factory> { + @Override + public SymbolDescriptor create(Pair p) { + if (p.first() instanceof AsyncDescriptor) { + return new RefreshableSymbolDescriptor(p.first(), p.second()); + } + return p.first(); + } + } } diff --git a/jumpto/src/org/netbeans/modules/jumpto/symbol/RefreshableSymbolDescriptor.java b/jumpto/src/org/netbeans/modules/jumpto/symbol/RefreshableSymbolDescriptor.java new file mode 100644 --- /dev/null +++ b/jumpto/src/org/netbeans/modules/jumpto/symbol/RefreshableSymbolDescriptor.java @@ -0,0 +1,144 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2015 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2015 Sun Microsystems, Inc. + */ +package org.netbeans.modules.jumpto.symbol; + +import javax.swing.Icon; +import javax.swing.SwingUtilities; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import org.netbeans.api.annotations.common.CheckForNull; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.annotations.common.NullAllowed; +import org.netbeans.spi.jumpto.support.AsyncDescriptor; +import org.netbeans.spi.jumpto.symbol.SymbolDescriptor; +import org.openide.filesystems.FileObject; + +/** + * + * @author Tomas Zezula + */ +final class RefreshableSymbolDescriptor extends SymbolDescriptor implements ChangeListener { + + private final SymbolDescriptor delegate; + private final Runnable refreshCallback; + + RefreshableSymbolDescriptor( + @NonNull final SymbolDescriptor delegate, + @NonNull final Runnable refreshCallback) { + assert delegate != null; + assert refreshCallback != null; + this.delegate = delegate; + this.refreshCallback = refreshCallback; + if (delegate instanceof AsyncDescriptor) { + ((AsyncDescriptor)delegate).addChangeListener(this); + } else { + throw new IllegalArgumentException(String.format( + "The delegate: %s is not an AsyncDescriptor", //NOI18N + delegate)); + } + } + + @Override + public Icon getIcon() { + return delegate.getIcon(); + } + + @Override + public String getSimpleName() { + return delegate.getSimpleName(); + } + + @Override + public String getSymbolName() { + return delegate.getSymbolName(); + } + + @Override + public String getOwnerName() { + return delegate.getOwnerName(); + } + + @Override + public String getProjectName() { + return delegate.getProjectName(); + } + + @Override + public Icon getProjectIcon() { + return delegate.getProjectIcon(); + } + + @Override + public FileObject getFileObject() { + return delegate.getFileObject(); + } + + @Override + public int getOffset() { + return delegate.getOffset(); + } + + @Override + public void open() { + delegate.open(); + } + + @Override + public void stateChanged(ChangeEvent e) { + runInEDT(this.refreshCallback); + } + + @CheckForNull + static SymbolDescriptor unwrap (@NullAllowed final SymbolDescriptor desc) { + return desc == null || desc.getClass() != RefreshableSymbolDescriptor.class ? + desc : + ((RefreshableSymbolDescriptor)desc).delegate; + } + + private static void runInEDT(@NonNull final Runnable r) { + if (SwingUtilities.isEventDispatchThread()) { + r.run(); + } else { + SwingUtilities.invokeLater(r); + } + } +} diff --git a/jumpto/src/org/netbeans/modules/jumpto/symbol/SymbolComparator.java b/jumpto/src/org/netbeans/modules/jumpto/symbol/SymbolComparator.java --- a/jumpto/src/org/netbeans/modules/jumpto/symbol/SymbolComparator.java +++ b/jumpto/src/org/netbeans/modules/jumpto/symbol/SymbolComparator.java @@ -42,6 +42,7 @@ package org.netbeans.modules.jumpto.symbol; +import org.netbeans.api.annotations.common.NonNull; import org.netbeans.modules.jumpto.EntityComparator; import org.netbeans.spi.jumpto.symbol.SymbolDescriptor; @@ -89,7 +90,7 @@ return result; // e1projectName NOT equals to e2projectName } // here: e1projectName equals to e2projectName - result = compare(e1.getSymbolName(), e2.getSymbolName()); + result = compare(getSortName(e1), getSortName(e2)); if ( result != 0 ) { return result; } @@ -97,4 +98,12 @@ return compare(e1.getOwnerName(), e2.getOwnerName()); } + @NonNull + private static String getSortName(@NonNull final SymbolDescriptor d) { + String res = d.getSimpleName(); + if (res == null) { + res = d.getSymbolName(); + } + return res; + } } diff --git a/jumpto/src/org/netbeans/spi/jumpto/support/AsyncDescriptor.java b/jumpto/src/org/netbeans/spi/jumpto/support/AsyncDescriptor.java new file mode 100644 --- /dev/null +++ b/jumpto/src/org/netbeans/spi/jumpto/support/AsyncDescriptor.java @@ -0,0 +1,54 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2015 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2015 Sun Microsystems, Inc. + */ +package org.netbeans.spi.jumpto.support; + +import javax.swing.event.ChangeListener; +import org.netbeans.api.annotations.common.NonNull; + +/** + * + * @author Tomas Zezula + */ +public interface AsyncDescriptor { + void addChangeListener(@NonNull ChangeListener listener); + void removeChangeListener(@NonNull ChangeListener listener); +}