--- a/api.java.classpath/apichanges.xml +++ a/api.java.classpath/apichanges.xml @@ -76,6 +76,22 @@ + + + Added ClassPath flags + + + + + +

+ Added flags to ClassPath +

+
+ + + +
Added a constant representing an empty ClassPath --- a/api.java.classpath/manifest.mf +++ a/api.java.classpath/manifest.mf @@ -1,5 +1,5 @@ Manifest-Version: 1.0 OpenIDE-Module: org.netbeans.api.java.classpath/1 OpenIDE-Module-Localizing-Bundle: org/netbeans/api/java/classpath/Bundle.properties -OpenIDE-Module-Specification-Version: 1.42 +OpenIDE-Module-Specification-Version: 1.43 --- a/api.java.classpath/nbproject/project.xml +++ a/api.java.classpath/nbproject/project.xml @@ -81,6 +81,8 @@ org.openide.util + + org.openide.util.lookup --- a/api.java.classpath/src/org/netbeans/api/java/classpath/ClassPath.java +++ a/api.java.classpath/src/org/netbeans/api/java/classpath/ClassPath.java @@ -77,6 +77,7 @@ import org.netbeans.spi.java.classpath.ClassPathImplementation; import org.netbeans.spi.java.classpath.ClassPathProvider; import org.netbeans.spi.java.classpath.FilteringPathResourceImplementation; +import org.netbeans.spi.java.classpath.FlaggedClassPathImplementation; import org.netbeans.spi.java.classpath.PathResourceImplementation; import org.netbeans.spi.java.classpath.support.ClassPathSupport; import org.openide.filesystems.FileAttributeEvent; @@ -206,7 +207,13 @@ * Name of the "entries" property */ public static final String PROP_ENTRIES = "entries"; - + + /** + * Name of the "flags" property + * @since 1.43 + */ + public static final String PROP_FLAGS = "flags"; + /** * Property to be fired when include/exclude set changes. * @see FilteringPathResourceImplementation @@ -596,6 +603,25 @@ } /** + * Returns the {@link ClassPath}'s flags. + * @return the {@link Flag}s + * @since 1.43 + */ + @NonNull + public Set getFlags() { + if (impl instanceof FlaggedClassPathImplementation) { + final Set res = ((FlaggedClassPathImplementation)impl).getFlags(); + assert res != null : String.format( + "ClassPathImplementation %s : %s returned null flags.", //NOI18N + impl, + impl.getClass()); + return res; + } else { + return Collections.emptySet(); + } + } + + /** * Find the classpath of a given type, if any, defined for a given file. *

This method may return null, if:

*
    @@ -676,6 +702,18 @@ } /** + * ClassPath's flags. + * @since 1.43 + */ + public enum Flag { + /** + * The incomplete {@link ClassPath} is ignored by language features + * unless it's resolved and the {@link INCOMPLETE} flag is removed. + */ + INCOMPLETE + } + + /** * Render this classpath in the conventional format used by the Java launcher. * @param conversionMode policy for converting unusual entries * @return a conventionally-formatted representation of the classpath @@ -1160,6 +1198,8 @@ if (fire) { firePropertyChange(PROP_INCLUDES, null, null, evt.getPropagationId()); } + } else if (FlaggedClassPathImplementation.PROP_FLAGS.equals(prop)) { + firePropertyChange(PROP_FLAGS, null, null, null); } if (ClassPathImplementation.PROP_RESOURCES.equals(prop)) { final List resources = impl.getResources(); --- a/api.java.classpath/src/org/netbeans/spi/java/classpath/FlaggedClassPathImplementation.java +++ a/api.java.classpath/src/org/netbeans/spi/java/classpath/FlaggedClassPathImplementation.java @@ -0,0 +1,67 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2014 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 2014 Sun Microsystems, Inc. + */ + +package org.netbeans.spi.java.classpath; + +import java.util.Set; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.api.java.classpath.ClassPath; + +/** + * The ClassPathImplementation with {@link ClassPath.Flag}s. + * @author Tomas Zezula + * @since 1.43 + */ +public interface FlaggedClassPathImplementation extends ClassPathImplementation { + + /** + * Name of the "flags" property. + */ + public static final String PROP_FLAGS = "flags"; //NOI18N + + /** + * Returns the {@link ClassPath}'s flags. + * @return the {@link Flag}s + */ + @NonNull + Set getFlags(); +} --- a/api.java.classpath/test/unit/src/org/netbeans/api/java/classpath/ClassPathTest.java +++ a/api.java.classpath/test/unit/src/org/netbeans/api/java/classpath/ClassPathTest.java @@ -56,6 +56,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.EnumSet; import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; @@ -77,6 +78,7 @@ import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.openide.util.Utilities; +import org.openide.util.test.MockPropertyChangeListener; public class ClassPathTest extends NbTestCase { @@ -312,7 +314,38 @@ f.delete(); assertEquals("again empty", null, cp.findResource("f")); } - + + public void testFlags() { + final FlaggedClassPathImpl fcpImpl = new FlaggedClassPathImpl(); + final ClassPath cp = ClassPathFactory.createClassPath(fcpImpl); + Set flags = cp.getFlags(); + assertNotNull(flags); + assertTrue(flags.isEmpty()); + fcpImpl.setFlags(EnumSet.of(ClassPath.Flag.INCOMPLETE)); + flags = cp.getFlags(); + assertNotNull(flags); + assertEquals(1, flags.size()); + assertEquals(ClassPath.Flag.INCOMPLETE, flags.iterator().next()); + fcpImpl.setFlags(EnumSet.noneOf(ClassPath.Flag.class)); + flags = cp.getFlags(); + assertNotNull(flags); + assertTrue(flags.isEmpty()); + } + + public void testFlagsFiring() { + final FlaggedClassPathImpl fcpImpl = new FlaggedClassPathImpl(); + final ClassPath cp = ClassPathFactory.createClassPath(fcpImpl); + Set flags = cp.getFlags(); + assertNotNull(flags); + assertTrue(flags.isEmpty()); + final MockPropertyChangeListener pl = new MockPropertyChangeListener(ClassPath.PROP_FLAGS); + cp.addPropertyChangeListener(pl); + fcpImpl.setFlags(EnumSet.of(ClassPath.Flag.INCOMPLETE)); + pl.assertEventCount(1); + fcpImpl.setFlags(EnumSet.noneOf(ClassPath.Flag.class)); + pl.assertEventCount(1); + } + static final class TestClassPathImplementation implements ClassPathImplementation, PropertyChangeListener { private final PropertyChangeSupport support = new PropertyChangeSupport (this); --- a/api.java.classpath/test/unit/src/org/netbeans/api/java/classpath/FlaggedClassPathImpl.java +++ a/api.java.classpath/test/unit/src/org/netbeans/api/java/classpath/FlaggedClassPathImpl.java @@ -0,0 +1,111 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2014 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 2014 Sun Microsystems, Inc. + */ + +package org.netbeans.api.java.classpath; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.Set; +import org.netbeans.api.annotations.common.NonNull; +import org.netbeans.spi.java.classpath.FlaggedClassPathImplementation; +import org.netbeans.spi.java.classpath.PathResourceImplementation; +import org.openide.util.Parameters; + +/** + * + * @author Tomas Zezula + */ +class FlaggedClassPathImpl implements FlaggedClassPathImplementation { + + private final PropertyChangeSupport support; + //@GuardedBy("this") + private Set flags; + //@GuardedBy("this") + private List resources; + + FlaggedClassPathImpl() { + this.support = new PropertyChangeSupport(this); + this.flags = EnumSet.noneOf(ClassPath.Flag.class); + this.resources = Collections.emptyList(); + } + + @Override + public synchronized Set getFlags() { + return flags; + } + + @Override + public synchronized List getResources() { + return resources; + } + + @Override + public void addPropertyChangeListener(PropertyChangeListener listener) { + Parameters.notNull("listener", listener); //NOI18N + this.support.addPropertyChangeListener(listener); + } + + @Override + public void removePropertyChangeListener(PropertyChangeListener listener) { + Parameters.notNull("listener", listener); //NOI18N + this.support.removePropertyChangeListener(listener); + } + + void setResources(@NonNull final List resources) { + Parameters.notNull("resources", resources); //NOI18N + synchronized (this) { + this.resources = resources; + } + support.firePropertyChange(PROP_RESOURCES, null, null); + } + + void setFlags(@NonNull final Set flags) { + Parameters.notNull("flags", flags); //NOI18N + synchronized (this) { + this.flags = flags; + } + support.firePropertyChange(PROP_FLAGS, null, null); + } +} --- a/java.source/nbproject/project.xml +++ a/java.source/nbproject/project.xml @@ -73,7 +73,7 @@ 1 - 1.0 + 1.43 --- a/java.source/src/org/netbeans/modules/java/source/classpath/SourcePathCheck.java +++ a/java.source/src/org/netbeans/modules/java/source/classpath/SourcePathCheck.java @@ -85,26 +85,30 @@ final CompilationInfo info = CompilationInfo.get(result); final ClasspathInfo cpInfo = info.getClasspathInfo(); if (cpInfo != null) { - final ClassPath cachedSrc = ClasspathInfoAccessor.getINSTANCE().getCachedClassPath(cpInfo, PathKind.SOURCE); final ClassPath src = cpInfo.getClassPath(PathKind.SOURCE); - try { - final Set unknown = new HashSet(); - if (cachedSrc.entries().isEmpty() && !src.entries().isEmpty()) { - for (ClassPath.Entry entry : src.entries()) { - final URL url = entry.getURL(); - if (!this.factory.firedFor.contains(url) && - !JavaIndex.hasSourceCache(url,false) && - FileOwnerQuery.getOwner(url.toURI()) != null) { - unknown.add(url); - this.factory.firedFor.add(url); + final ClassPath boot = cpInfo.getClassPath(PathKind.BOOT); + final ClassPath compile = cpInfo.getClassPath(PathKind.COMPILE); + if (!isIncomplete(src, boot, compile)) { + final ClassPath cachedSrc = ClasspathInfoAccessor.getINSTANCE().getCachedClassPath(cpInfo, PathKind.SOURCE); + try { + final Set unknown = new HashSet(); + if (cachedSrc.entries().isEmpty() && !src.entries().isEmpty()) { + for (ClassPath.Entry entry : src.entries()) { + final URL url = entry.getURL(); + if (!this.factory.firedFor.contains(url) && + !JavaIndex.hasSourceCache(url,false) && + FileOwnerQuery.getOwner(url.toURI()) != null) { + unknown.add(url); + this.factory.firedFor.add(url); + } } - } + } + if (!unknown.isEmpty()) { + PathRegistry.getDefault().registerUnknownSourceRoots(src, unknown); + } + } catch (URISyntaxException e) { + Exceptions.printStackTrace(e); } - if (!unknown.isEmpty()) { - PathRegistry.getDefault().registerUnknownSourceRoots(src, unknown); - } - } catch (URISyntaxException e) { - Exceptions.printStackTrace(e); } } } @@ -123,6 +127,15 @@ public void cancel() { } + private static boolean isIncomplete(ClassPath... cps) { + for (ClassPath cp : cps) { + if (cp.getFlags().contains(ClassPath.Flag.INCOMPLETE)) { + return true; + } + } + return false; + } + public static final class Factory extends TaskFactory { @org.netbeans.api.annotations.common.SuppressWarnings(value={"DMI_COLLECTION_OF_URLS"}, justification="URLs have never host part") //NOI18N --- a/java.source/src/org/netbeans/modules/java/source/parsing/Bundle.properties +++ a/java.source/src/org/netbeans/modules/java/source/parsing/Bundle.properties @@ -42,3 +42,4 @@ # made subject to such option by the copyright holder. # FMT_InvalidFile=Invalid or deleted file: {0} +ERR_IncompleteClassPath=Missing mandatory Classpath entries. Resolve Project Problems. --- a/java.source/src/org/netbeans/modules/java/source/parsing/CompilationInfoImpl.java +++ a/java.source/src/org/netbeans/modules/java/source/parsing/CompilationInfoImpl.java @@ -53,6 +53,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.EnumMap; import java.util.HashMap; import java.util.Iterator; @@ -68,18 +69,23 @@ import javax.tools.Diagnostic.Kind; import javax.tools.DiagnosticListener; import javax.tools.JavaFileObject; +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.CompilationInfo; import org.netbeans.api.java.source.CompilationInfo.CacheClearPolicy; import org.netbeans.api.java.source.JavaSource; import org.netbeans.api.lexer.TokenHierarchy; import org.netbeans.modules.java.source.JavaFileFilterQuery; +import org.netbeans.modules.java.source.indexing.JavaIndex; import org.netbeans.modules.parsing.api.Snapshot; import org.openide.cookies.EditorCookie; import org.openide.filesystems.FileObject; import org.openide.loaders.DataObject; import org.openide.loaders.DataObjectNotFoundException; import org.openide.util.Exceptions; +import org.openide.util.NbBundle; import org.openide.util.Pair; /** @@ -397,7 +403,7 @@ */ public synchronized JavacTaskImpl getJavacTask() { if (javacTask == null) { - diagnosticListener = new DiagnosticListenerImpl(this.jfo); + diagnosticListener = new DiagnosticListenerImpl(this.root, this.jfo, this.cpInfo); javacTask = JavacParser.createJavacTask(this.file, this.root, this.cpInfo, this.parser, diagnosticListener, null, isDetached); } @@ -482,7 +488,9 @@ static class DiagnosticListenerImpl implements DiagnosticListener { private final Map>>> source2Errors; + private final FileObject root; private final JavaFileObject jfo; + private final ClasspathInfo cpInfo; private volatile List> partialReparseErrors; /** * true if the partialReparseErrors contain some non-warning @@ -491,8 +499,13 @@ private volatile List> affectedErrors; private volatile int currentDelta; - public DiagnosticListenerImpl(final JavaFileObject jfo) { + public DiagnosticListenerImpl( + @NullAllowed final FileObject root, + @NullAllowed final JavaFileObject jfo, + @NonNull final ClasspathInfo cpInfo) { + this.root = root; this.jfo = jfo; + this.cpInfo = cpInfo; this.source2Errors = new HashMap>>>(); } @@ -518,13 +531,33 @@ } private TreeMap>> getErrors(JavaFileObject file) { - TreeMap>> errors = source2Errors.get(file); - + TreeMap>> errors; + if (isIncompleteClassPath()) { + if (root != null && JavaIndex.hasSourceCache(root.toURL(), false)) { + errors = source2Errors.get(file); + if (errors == null) { + source2Errors.put(file, errors = new TreeMap>>()); + if (this.jfo != null && this.jfo == file) { + errors.put( + -1, + Collections.>singleton(new IncompleteClassPath(this.jfo))); + } + } + } else { + errors = new TreeMap<>(); + if (this.jfo != null && this.jfo == file) { + errors.put( + -1, + Collections.>singleton(new IncompleteClassPath(this.jfo))); + } + } + } else { + errors = source2Errors.get(file); if (errors == null) { source2Errors.put(file, errors = new TreeMap>>()); } - - return errors; + } + return errors; } final boolean hasPartialReparseErrors () { @@ -567,8 +600,14 @@ result.put(entry.getKey(), Arrays.asList(entry.getValue())); } return result; - } - + } + + private boolean isIncompleteClassPath() { + return cpInfo.getClassPath(ClasspathInfo.PathKind.BOOT).getFlags().contains(ClassPath.Flag.INCOMPLETE) || + cpInfo.getClassPath(ClasspathInfo.PathKind.COMPILE).getFlags().contains(ClassPath.Flag.INCOMPLETE) || + cpInfo.getClassPath(ClasspathInfo.PathKind.SOURCE).getFlags().contains(ClassPath.Flag.INCOMPLETE); + } + private final class D implements Diagnostic { private final JCDiagnostic delegate; @@ -636,6 +675,62 @@ } } + + private static final class IncompleteClassPath implements Diagnostic { + + private final JavaFileObject file; + + IncompleteClassPath(final JavaFileObject file) { + this.file = file; + } + + @Override + public Kind getKind() { + return Kind.ERROR; + } + + @Override + public JavaFileObject getSource() { + return file; + } + + @Override + public long getPosition() { + return -1; + } + + @Override + public long getStartPosition() { + return getPosition(); + } + + @Override + public long getEndPosition() { + return getPosition(); + } + + @Override + public long getLineNumber() { + return getPosition(); + } + + @Override + public long getColumnNumber() { + return getPosition(); + } + + @Override + public String getCode() { + return "nb.classpath.incomplete"; //NOI18N + } + + @Override + public String getMessage(Locale locale) { + return NbBundle.getMessage( + CompilationInfoImpl.class, + "ERR_IncompleteClassPath"); + } + } } static final class RichDiagnostic implements Diagnostic { --- a/maven/nbproject/project.xml +++ a/maven/nbproject/project.xml @@ -104,7 +104,7 @@ 1 - 1.18 + 1.43 --- a/maven/src/org/netbeans/modules/maven/classpath/AbstractProjectClassPathImpl.java +++ a/maven/src/org/netbeans/modules/maven/classpath/AbstractProjectClassPathImpl.java @@ -141,6 +141,10 @@ protected final NbMavenProjectImpl getMavenProject() { return project; } + + protected final void firePropertyChange(String propName, Object oldValue, Object newValue) { + support.firePropertyChange(propName, oldValue, newValue); + } @Override public synchronized List getResources() { --- a/maven/src/org/netbeans/modules/maven/classpath/CompileClassPathImpl.java +++ a/maven/src/org/netbeans/modules/maven/classpath/CompileClassPathImpl.java @@ -44,17 +44,24 @@ import java.net.URI; import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; import java.util.List; +import java.util.Set; import org.apache.maven.artifact.Artifact; +import org.netbeans.api.java.classpath.ClassPath; import org.netbeans.modules.maven.NbMavenProjectImpl; +import org.netbeans.spi.java.classpath.FlaggedClassPathImplementation; import org.openide.util.Utilities; /** * * @author Milos Kleint */ -class CompileClassPathImpl extends AbstractProjectClassPathImpl { - +class CompileClassPathImpl extends AbstractProjectClassPathImpl implements FlaggedClassPathImplementation { + + private volatile boolean incomplete; + /** Creates a new instance of SrcClassPathImpl */ public CompileClassPathImpl(NbMavenProjectImpl proj) { super(proj); @@ -67,16 +74,29 @@ //except for the fact that multiproject references are not redirected to their respective // output folders.. we lways retrieve stuff from local repo.. List arts = getMavenProject().getOriginalMavenProject().getCompileArtifacts(); + boolean broken = false; for (Artifact art : arts) { if (art.getFile() != null) { lst.add(Utilities.toURI(art.getFile())); + broken |= !art.getFile().exists(); } else { //NOPMD //null means dependencies were not resolved.. + broken = true; } } + if (incomplete != broken) { + incomplete = broken; + firePropertyChange(PROP_FLAGS, null, null); + } URI[] uris = new URI[lst.size()]; uris = lst.toArray(uris); return uris; } - + + @Override + public Set getFlags() { + return incomplete ? + EnumSet.of(ClassPath.Flag.INCOMPLETE) : + Collections.emptySet(); + } } --- a/maven/src/org/netbeans/modules/maven/classpath/TestCompileClassPathImpl.java +++ a/maven/src/org/netbeans/modules/maven/classpath/TestCompileClassPathImpl.java @@ -44,17 +44,24 @@ import java.net.URI; import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; import java.util.List; +import java.util.Set; import org.apache.maven.artifact.Artifact; +import org.netbeans.api.java.classpath.ClassPath; import org.netbeans.modules.maven.NbMavenProjectImpl; +import org.netbeans.spi.java.classpath.FlaggedClassPathImplementation; import org.openide.util.Utilities; /** * * @author Milos Kleint */ -class TestCompileClassPathImpl extends AbstractProjectClassPathImpl { - +class TestCompileClassPathImpl extends AbstractProjectClassPathImpl implements FlaggedClassPathImplementation { + + private volatile boolean incomplete; + /** Creates a new instance of SrcClassPathImpl */ public TestCompileClassPathImpl(NbMavenProjectImpl proj) { super(proj); @@ -69,17 +76,30 @@ //except for the fact that multiproject references are not redirected to their respective // output folders.. we lways retrieve stuff from local repo.. List arts = getMavenProject().getOriginalMavenProject().getTestArtifacts(); + boolean broken = false; for (Artifact art : arts) { if (art.getFile() != null) { lst.add(Utilities.toURI(art.getFile())); + broken |= !art.getFile().exists(); } else { //NOPMD //null means dependencies were not resolved.. - } + broken = true; + } + } + if (incomplete != broken) { + incomplete = broken; + firePropertyChange(PROP_FLAGS, null, null); } lst.add(0, Utilities.toURI(getMavenProject().getProjectWatcher().getOutputDirectory(false))); URI[] uris = new URI[lst.size()]; uris = lst.toArray(uris); return uris; } - + + @Override + public Set getFlags() { + return incomplete ? + EnumSet.of(ClassPath.Flag.INCOMPLETE) : + Collections.emptySet(); + } } --- a/parsing.api/nbproject/project.xml +++ a/parsing.api/nbproject/project.xml @@ -20,7 +20,7 @@ 1 - 1.18 + 1.43 --- a/parsing.api/src/org/netbeans/modules/parsing/impl/indexing/PathRegistry.java +++ a/parsing.api/src/org/netbeans/modules/parsing/impl/indexing/PathRegistry.java @@ -77,6 +77,7 @@ import org.openide.filesystems.FileObject; import org.openide.filesystems.FileStateInvalidException; import org.openide.filesystems.FileUtil; +import org.openide.filesystems.URLMapper; import org.openide.util.Exceptions; import org.openide.util.Parameters; import org.openide.util.RequestProcessor; @@ -541,6 +542,52 @@ url.getProtocol()); } + static boolean isIncompleteClassPath(@NonNull final ClassPath cp) { + return cp.getFlags().contains(ClassPath.Flag.INCOMPLETE); + } + + boolean isIncompleteRoot(@NonNull final FileObject root) { + Set sourceIds; + Set libIds; + Set binLibIds; + + if ((sourceIds = getSourceIdsFor(root.toURL())) != null && !sourceIds.isEmpty()) { + libIds = new HashSet<>(); + binLibIds = new HashSet<>(); + for(String id : sourceIds) { + libIds.addAll(PathRecognizerRegistry.getDefault().getLibraryIdsForSourceId(id)); + binLibIds.addAll(PathRecognizerRegistry.getDefault().getBinaryLibraryIdsForSourceId(id)); + } + } else if ((libIds = getLibraryIdsFor(root.toURL())) != null && !libIds.isEmpty()) { + sourceIds = Collections.emptySet(); + binLibIds = new HashSet<>(); + for(String id : libIds) { + binLibIds.addAll(PathRecognizerRegistry.getDefault().getBinaryLibraryIdsForLibraryId(id)); + } + } else { + sourceIds = PathRecognizerRegistry.getDefault().getSourceIds(); + libIds = new HashSet<>(); + binLibIds = new HashSet<>(); + for(String id : sourceIds) { + libIds.addAll(PathRecognizerRegistry.getDefault().getLibraryIdsForSourceId(id)); + binLibIds.addAll(PathRecognizerRegistry.getDefault().getBinaryLibraryIdsForSourceId(id)); + } + } + assert sourceIds != null; + assert libIds != null; + assert binLibIds != null; + return isIncompleteClassPath(root, binLibIds) || + isIncompleteClassPath(root, libIds) || + isIncompleteClassPath(root, sourceIds); + } + + boolean isIncompleteRoot(@NonNull final URL root) { + final FileObject rootFo = URLMapper.findFileObject(root); + return rootFo == null ? + false : + isIncompleteRoot(rootFo); + } + @org.netbeans.api.annotations.common.SuppressWarnings( value="DMI_COLLECTION_OF_URLS", justification="URLs have never host part") @@ -643,14 +690,9 @@ if (oldHash == null) { return true; } - final StringBuilder newRoots = new StringBuilder(); - for (ClassPath.Entry e : cp.entries()) { - newRoots.append(e.getURL().toExternalForm()). - append(File.pathSeparatorChar); - } return !Arrays.equals( oldHash, - toDigest(createDigest(),newRoots)); + toDigest(cp, createDigest())); } @org.netbeans.api.annotations.common.SuppressWarnings( @@ -672,16 +714,15 @@ for (TaggedClassPath tcp : request.sourceCps) { ClassPath cp = tcp.getClasspath(); boolean isNew = request.oldCps.remove(cp) == null; - final StringBuilder sb = new StringBuilder(); for (ClassPath.Entry entry : cp.entries()) { URL root = entry.getURL(); assert noHostPart(root) : root; - sb.append(root.toExternalForm()). - append(File.pathSeparatorChar); - sourceResult.add(root); updatePathIds(root, tcp, pathIdsResult, pathIdToRootsResult); + if (!isIncompleteClassPath(cp)) { + sourceResult.add(root); + } } - boolean notContained = newCps.put (cp, toDigest(digest, sb)) == null; + boolean notContained = newCps.put (cp, toDigest(cp,digest)) == null; if (isNew && notContained) { cp.addPropertyChangeListener(request.propertyListener); } @@ -690,16 +731,15 @@ for (TaggedClassPath tcp : request.libraryCps) { ClassPath cp = tcp.getClasspath(); boolean isNew = request.oldCps.remove(cp) == null; - final StringBuilder sb = new StringBuilder(); for (ClassPath.Entry entry : cp.entries()) { URL root = entry.getURL(); assert noHostPart(root) : root; - sb.append(root.toExternalForm()). - append(File.pathSeparatorChar); - libraryResult.add(root); updatePathIds(root, tcp, pathIdsResult, pathIdToRootsResult); + if (!isIncompleteClassPath(cp)) { + libraryResult.add(root); + } } - boolean notContained = newCps.put (cp, toDigest(digest, sb)) == null; + boolean notContained = newCps.put (cp, toDigest(cp, digest)) == null; if (isNew && notContained) { cp.addPropertyChangeListener(request.propertyListener); } @@ -708,15 +748,12 @@ for (TaggedClassPath tcp : request.binaryLibraryCps) { ClassPath cp = tcp.getClasspath(); boolean isNew = request.oldCps.remove(cp) == null; - final StringBuilder sb = new StringBuilder(); for (ClassPath.Entry entry : cp.entries()) { URL binRoot = entry.getURL(); assert noHostPart(binRoot) : binRoot; - sb.append(binRoot.toExternalForm()). - append(File.pathSeparatorChar); if (!translatedRoots.containsKey(binRoot)) { updatePathIds(binRoot, tcp, pathIdsResult, pathIdToRootsResult); - + SourceForBinaryQuery.Result2 sr = request.oldSR.remove (binRoot); boolean isNewSR; if (sr == null) { @@ -732,9 +769,13 @@ final Set cacheURLs = new LinkedHashSet (); //LinkedSet to protect against wrong SFBQ but keep ordering final Collection srcRoots = getSources(sr, cacheURLs, request.unknownRoots); if (srcRoots.isEmpty()) { - binaryLibraryResult.add(binRoot); + if (!isIncompleteClassPath(cp)) { + binaryLibraryResult.add(binRoot); + } } else { - libraryResult.addAll(srcRoots); + if (!isIncompleteClassPath(cp)) { + libraryResult.addAll(srcRoots); + } updateTranslatedPathIds(srcRoots, tcp, pathIdsResult, pathIdToRootsResult); } translatedRoots.put(binRoot, cacheURLs.toArray(new URL[cacheURLs.size()])); @@ -747,7 +788,7 @@ } } } - boolean notContained = newCps.put (cp, toDigest(digest, sb)) == null; + boolean notContained = newCps.put (cp, toDigest(cp, digest)) == null; if (isNew && notContained) { cp.addPropertyChangeListener(request.propertyListener); } @@ -861,8 +902,17 @@ @NonNull private static byte[] toDigest( - @NonNull final MessageDigest md, - @NonNull final StringBuilder sb) { + @NonNull final ClassPath cp, + @NonNull final MessageDigest md) { + final StringBuilder sb = new StringBuilder(); + for (ClassPath.Flag flag : cp.getFlags()) { + sb.append(flag.name()). + append(File.pathSeparatorChar); + } + for (ClassPath.Entry e : cp.entries()) { + sb.append(e.getURL().toExternalForm()). + append(File.pathSeparatorChar); + } try { return md.digest(sb.toString().getBytes()); } finally { @@ -870,6 +920,18 @@ } } + private static boolean isIncompleteClassPath( + @NonNull final FileObject root, + @NonNull final Set classPathTypes) { + for (String classPathType : classPathTypes) { + final ClassPath cp = ClassPath.getClassPath(root, classPathType); + if (cp != null && isIncompleteClassPath(cp)) { + return true; + } + } + return false; + } + @NonNull private static MessageDigest createDigest() { try { @@ -1188,7 +1250,7 @@ } String propName = evt.getPropertyName(); - if (ClassPath.PROP_ENTRIES.equals(propName)) { + if (ClassPath.PROP_ENTRIES.equals(propName) || ClassPath.PROP_FLAGS.equals(propName)) { final Object source = evt.getSource(); if ((source instanceof ClassPath) && classPathChanged((ClassPath)source)) { --- a/parsing.api/src/org/netbeans/modules/parsing/impl/indexing/RepositoryUpdater.java +++ a/parsing.api/src/org/netbeans/modules/parsing/impl/indexing/RepositoryUpdater.java @@ -1759,6 +1759,7 @@ final List deps = new LinkedList(); final List peers = new LinkedList(); + boolean incomplete = false; ctx.cycleDetector.push(rootURL); try { if (sourceIds == null || libraryIds == null || binaryLibraryIds == null) { @@ -1815,6 +1816,7 @@ } final ClassPath cp = ClassPath.getClassPath(rootFo, id); if (cp != null) { + incomplete |= PathRegistry.isIncompleteClassPath(cp); for (ClassPath.Entry entry : cp.entries()) { if (cancelRequest.isRaised()) { return false; @@ -1837,6 +1839,7 @@ ClassPath cp = ClassPath.getClassPath(rootFo, id); if (cp != null) { + incomplete |= PathRegistry.isIncompleteClassPath(cp); for (ClassPath.Entry entry : cp.entries()) { if (cancelRequest.isRaised()) { return false; @@ -1875,6 +1878,7 @@ ClassPath cp = ClassPath.getClassPath(rootFo, id); if (cp != null) { + incomplete |= PathRegistry.isIncompleteClassPath(cp); for (ClassPath.Entry entry : cp.entries()) { if (cancelRequest.isRaised()) { return false; @@ -1958,9 +1962,10 @@ } finally { ctx.cycleDetector.pop(); } - - ctx.newRoots2Deps.put(rootURL, deps); - ctx.newRoots2Peers.put(rootURL,peers); + if (!incomplete) { + ctx.newRoots2Deps.put(rootURL, deps); + ctx.newRoots2Peers.put(rootURL,peers); + } return true; } @@ -3610,29 +3615,32 @@ } protected @Override boolean getDone() { - updateProgress(root, false); - if (scanFiles(root, files, forceRefresh, sourceForBinaryRoot)) { - // if we are refreshing a specific set of files, try to update - // their document versions - if (!files.isEmpty()) { - Map f2d = getEditorFiles(); - for(FileObject f : files) { - Document d = f2d.get(f); - if (d != null) { - long version = DocumentUtilities.getDocumentVersion(d); - d.putProperty(PROP_LAST_INDEXED_VERSION, version); - d.putProperty(PROP_LAST_DIRTY_VERSION, null); + if (scannedRoots2Depencencies.keySet().contains(root) || + !PathRegistry.getDefault().isIncompleteRoot(root)) { + updateProgress(root, false); + if (scanFiles(root, files, forceRefresh, sourceForBinaryRoot)) { + // if we are refreshing a specific set of files, try to update + // their document versions + if (!files.isEmpty()) { + Map f2d = getEditorFiles(); + for(FileObject f : files) { + Document d = f2d.get(f); + if (d != null) { + long version = DocumentUtilities.getDocumentVersion(d); + d.putProperty(PROP_LAST_INDEXED_VERSION, version); + d.putProperty(PROP_LAST_DIRTY_VERSION, null); + } } } - } - - //If the root is unknown add it into scannedRoots2Depencencies to allow listening on changes under this root - if (!scannedRoots2Depencencies.containsKey(root)) { - scannedRoots2Depencencies.put(root, UNKNOWN_ROOT); - } - } - TEST_LOGGER.log(Level.FINEST, "filelist"); //NOI18N - refreshActiveDocument(); + + //If the root is unknown add it into scannedRoots2Depencencies to allow listening on changes under this root + if (!scannedRoots2Depencencies.containsKey(root)) { + scannedRoots2Depencencies.put(root, UNKNOWN_ROOT); + } + } + TEST_LOGGER.log(Level.FINEST, "filelist"); //NOI18N + refreshActiveDocument(); + } return true; }