This is an automated email from the ASF dual-hosted git repository.

sdedic pushed a commit to branch sdedic/feature/project-dependency-add_base2
in repository https://gitbox.apache.org/repos/asf/netbeans.git

commit 1b8c5c656bd49ae07bb2441fe1c45e48e1cf47c9
Author: Svata Dedic <[email protected]>
AuthorDate: Wed Dec 13 17:08:51 2023 +0100

    Scope redefinition, private API break.
---
 .../modules/gradle/api/GradleConfiguration.java    |  11 +-
 .../nbproject/project.properties                   |   2 +-
 .../project/dependency/DependencyResult.java       |  10 +-
 .../modules/project/dependency/ProjectScopes.java  |  49 ++++++
 .../netbeans/modules/project/dependency/Scope.java |  49 +++---
 .../modules/project/dependency/Scopes.java         |  88 ++++------
 java/gradle.java/nbproject/project.xml             |   7 +
 .../gradle/java/queries/DependencyText.java        |  63 ++++++-
 .../queries/GradleDependenciesImplementation.java  | 191 +++++++++++++++++----
 .../java/queries/GradleDependencyResult.java       |  59 ++++---
 .../modules/gradle/java/queries/GradleScope.java   |  81 +++++++++
 .../modules/gradle/java/queries/GradleScopes.java  |  66 +++++++
 .../gradle/java/queries/GradleScopesBuilder.java   | 129 ++++++++++++++
 .../gradle/java/queries/TextDependencyScanner.java |  50 ++++--
 .../unit/data/dependencies/micronaut/build.gradle  |  10 +-
 .../data/dependencies/parse/variousSyntax.gradle   |  12 +-
 .../GradleDependenciesImplementationTest.java      | 123 ++++++++++++-
 .../java/queries/RegexpGradleScannerTest.java      |  77 ++++++++-
 .../queries/MavenDependenciesImplementation.java   | 134 +++++++++++----
 .../maven/queries/MavenDependencyResult.java       |  19 +-
 20 files changed, 1011 insertions(+), 219 deletions(-)

diff --git 
a/extide/gradle/src/org/netbeans/modules/gradle/api/GradleConfiguration.java 
b/extide/gradle/src/org/netbeans/modules/gradle/api/GradleConfiguration.java
index 3c21520b72..6b795428c1 100644
--- a/extide/gradle/src/org/netbeans/modules/gradle/api/GradleConfiguration.java
+++ b/extide/gradle/src/org/netbeans/modules/gradle/api/GradleConfiguration.java
@@ -85,7 +85,11 @@ public final class GradleConfiguration implements 
Serializable, ModuleSearchSupp
      * @return direct dependencies
      */
     public Collection<? extends GradleDependency> getConfiguredDependencies() {
-        return directChildren;
+        if (canBeResolved) {
+            return directChildren;
+        } else {
+            return unresolved;
+        }
     }
     
     /**
@@ -100,12 +104,13 @@ public final class GradleConfiguration implements 
Serializable, ModuleSearchSupp
      * @return configuration of origin or {@code null}.
      */
     public GradleConfiguration getDependencyOrigin(GradleDependency d) {
-        if (!getDependencies().contains(d)) {
+        if (!getDependencies().contains(d) && 
!getConfiguredDependencies().contains(d)) {
             return null;
         }
         // TODO: possibly create a dependency-to-config cache in this instance 
to speed up further queries
         Set<GradleConfiguration> done = new HashSet<>();
-        Queue<GradleConfiguration> toProcess = new 
ArrayDeque<>(getExtendsFrom());
+        Queue<GradleConfiguration> toProcess = new ArrayDeque<>();
+        toProcess.add(this);
         
         GradleConfiguration conf;
         while ((conf = toProcess.poll()) != null) {
diff --git a/ide/project.dependency/nbproject/project.properties 
b/ide/project.dependency/nbproject/project.properties
index 209dfe92e4..264c46ce0c 100644
--- a/ide/project.dependency/nbproject/project.properties
+++ b/ide/project.dependency/nbproject/project.properties
@@ -18,4 +18,4 @@
 is.autoload=true
 javac.source=1.8
 javac.compilerargs=-Xlint -Xlint:-serial
-spec.version.base=1.6.0
+spec.version.base=1.7.0
diff --git 
a/ide/project.dependency/src/org/netbeans/modules/project/dependency/DependencyResult.java
 
b/ide/project.dependency/src/org/netbeans/modules/project/dependency/DependencyResult.java
index e59c573805..7181e6b627 100644
--- 
a/ide/project.dependency/src/org/netbeans/modules/project/dependency/DependencyResult.java
+++ 
b/ide/project.dependency/src/org/netbeans/modules/project/dependency/DependencyResult.java
@@ -38,6 +38,8 @@ import org.openide.util.Lookup;
  * <p>
  * The {@link #getLookup() lookup} can be used to search for project-specific 
services that
  * can provide further info on the artifacts or dependencies.
+ * 
+ * PENDING: move to SPI, make API delegating wrapper.
  * @author sdedic
  */
 public interface DependencyResult extends Lookup.Provider {
@@ -119,7 +121,7 @@ public interface DependencyResult extends Lookup.Provider {
     /**
      * A special part that locates a location appropriate for the surrounding
      * container. For example {@code dependencies} element in Maven or {@code 
dependencies}
-     * block in a gradle script. Use project root as the dependency
+     * block in a gradle script. Use project root or {@code null} as the 
dependency
      */
     public static final String PART_CONTAINER = "container"; // NOI18N
 
@@ -131,4 +133,10 @@ public interface DependencyResult extends Lookup.Provider {
      * source location can not be determined.
      */
     public @CheckForNull SourceLocation getDeclarationRange(@NonNull 
Dependency d, String part) throws IOException;
+    
+    /**
+     * Returns description of project scopes.
+     * @return project scopes.
+     */
+    public ProjectScopes getScopes();
 }
diff --git 
a/ide/project.dependency/src/org/netbeans/modules/project/dependency/ProjectScopes.java
 
b/ide/project.dependency/src/org/netbeans/modules/project/dependency/ProjectScopes.java
new file mode 100644
index 0000000000..8823de4ce6
--- /dev/null
+++ 
b/ide/project.dependency/src/org/netbeans/modules/project/dependency/ProjectScopes.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.project.dependency;
+
+import java.util.Collection;
+
+/**
+ * Describes scopes supported by the project. 
+ * PENDING: move to SPI; make an API final delegating counterpart / wrapper.
+ * @author sdedic
+ * @since 1.7
+ */
+public interface ProjectScopes {
+    /**
+     * Returns the set of supported scopes. The returned set should include 
+     * those abstract scopes supported by the project. Note that if additional
+     * plugins are added to the build system, the set of scopes may change.
+     * 
+     * @return set of supported scopes.
+     */
+    public Collection<? extends Scope>  scopes();
+    
+    /**
+     * Returns the scopes that this one implies. Note that the the {@code 
implies}
+     * relation need not to be transitive (i.e. some scopes may be filtered 
from
+     * further inheritance).
+     * 
+     * @param s the scope
+     * @param s direct if true, just direct implications are returned.
+     * @return 
+     */
+    public Collection<? extends Scope> implies(Scope s, boolean direct);
+}
diff --git 
a/ide/project.dependency/src/org/netbeans/modules/project/dependency/Scope.java 
b/ide/project.dependency/src/org/netbeans/modules/project/dependency/Scope.java
index 85e9161c0d..bdefe6aaa2 100644
--- 
a/ide/project.dependency/src/org/netbeans/modules/project/dependency/Scope.java
+++ 
b/ide/project.dependency/src/org/netbeans/modules/project/dependency/Scope.java
@@ -28,42 +28,17 @@ import java.util.Objects;
  * Scopes are identified by its {@link #name name}; two scopes with the same 
name are equal.
  * Project implementations may provide their own scopes with standard names 
since they
  * may use different include/imply hierarchy.
- * 
+ * <p/>
+ * Scope instances created by the build system 
  * @author sdedic
  */
-public abstract class Scope {
+public class Scope {
     private final String name;
 
     protected Scope(String name) {
         this.name = name;
     }
     
-    /**
-     * Checks if this scope includes the other one. If yes, then queries that 
executed for this scope
-     * should return all results from the included scope.
-     * 
-     * @param s scope to test
-     * @return true, if data for scope "s" are included by this scope; false 
otherwise (i.e. unrelated scopes)
-     */
-    public abstract boolean includes(Scope s);
-    
-    /**
-     * Checks if this scope exports the other scope. A scope may {@link 
#includes include} other
-     * scope, but can choose not to propagate its contents further.
-     * @param s the scope to test
-     * @return true, if data for scope "s" are exported by this scope; false 
otherwise (i.e. unrelated scopes)
-     */
-    public abstract boolean exports(Scope s);
-    
-    /**
-     * Determines if artifacts in this scope apply to the other one. This is 
the reverse of {@link includes} and
-     * allows injection to existing scopes.
-     * 
-     * @param s the scope to test.
-     * @return true, if the scope 's' is implied (includes) this one.
-     */
-    public abstract boolean implies(Scope s);
-    
     /**
      * @return name / identifier for the scope. Not subject to L10N.
      */
@@ -92,4 +67,22 @@ public abstract class Scope {
         final Scope other = (Scope) obj;
         return Objects.equals(this.name, other.name);
     }
+
+    // this behaviour is used in tests, change (in subclasses) carefully.
+    @Override
+    public String toString() {
+        return name();
+    }
+    
+    /**
+     * Creates a named scope. Callers should strongly prefer either abstract 
scopes
+     * declared in {@link Scopes}, or get supported scopes from the project / 
build system.
+     * Instances created by this method can only serve as handles / 
identifiers.
+     * 
+     * @param id scope Id
+     * @return scope
+     */
+    public static Scope named(String id) {
+        return new Scope(id);
+    }
 }
diff --git 
a/ide/project.dependency/src/org/netbeans/modules/project/dependency/Scopes.java
 
b/ide/project.dependency/src/org/netbeans/modules/project/dependency/Scopes.java
index 0ca68010f3..cb8f79accd 100644
--- 
a/ide/project.dependency/src/org/netbeans/modules/project/dependency/Scopes.java
+++ 
b/ide/project.dependency/src/org/netbeans/modules/project/dependency/Scopes.java
@@ -18,11 +18,6 @@
  */
 package org.netbeans.modules.project.dependency;
 
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
 /**
  *
  * @author sdedic
@@ -31,82 +26,57 @@ public final class Scopes {
     /**
      * Build process dependencies. Annotation processors, buildtime tools, 
code generators
      */
-    public static final Scope PROCESS = new DefaultScope("compileProcessing", 
Collections.emptySet(), Collections.emptySet());
+    public static final Scope PROCESS = new Scope("compileProcessing");
 
     /**
      * External dependencies, not distributed with the application, but 
provided by the environment (= provided dependencies in Maven)
      */
-    public static final Scope EXTERNAL = new DefaultScope("external", 
Collections.emptySet(), Collections.emptySet());
+    public static final Scope EXTERNAL = new Scope("external");
     
     /**
-     * Compile dependencies. Resources used by build tools to build the 
application. Includes 
-     * {@link #PROCESS} but does not export it further.
+     * Compile API dependencies. Optional, if the build system supports it. 
Otherwise should be equal to 
+     * {@link #COMPILE}. Gradle makes a difference between API and 
implementation. 
      */
-    public static final Scope COMPILE = new DefaultScope("compilation", 
-            Collections.singleton(PROCESS), Collections.singleton(PROCESS));
+    public static final Scope API = new Scope("api");
     
     /**
-     * Runtime dependencies. Includes compile dependencies.
+     * Compile dependencies. Resources used by build tools to build the 
application. 
      */
-    public static final Scope RUNTIME = new DefaultScope("runtime", 
Collections.singleton(COMPILE), Collections.emptySet());
+    public static final Scope COMPILE = new Scope("compilation");
     
     /**
-     * Test compile dependencies.
+     * Runtime dependencies. Includes compile dependencies, but not 
necessarily all of them.
      */
-    public static final Scope TEST_COMPILE = new DefaultScope("testCompile", 
-            new HashSet<>(Arrays.asList(PROCESS, COMPILE)), 
Collections.emptySet());
+    public static final Scope RUNTIME = new Scope("runtime");
     
     /**
-     * Test compile dependencies.
+     * Test compile dependencies. Optional, if the build system supports it.
      */
-    public static final Scope TEST_RUNTIME = new DefaultScope("testRuntime", 
-            new HashSet<>(Arrays.asList(TEST_COMPILE)), 
Collections.emptySet());
+    public static final Scope TEST_COMPILE = new Scope("testCompile");
     
     /**
-     * Test dependencies.
+     * Test runtime dependencies. Optional, if the build system supports it.
      */
-    public static final Scope TEST = new DefaultScope("test", 
-            new HashSet<>(Arrays.asList(PROCESS, COMPILE, RUNTIME)), 
Collections.emptySet()).imply(TEST_RUNTIME, TEST_COMPILE);
+    public static final Scope TEST_RUNTIME = new Scope("testRuntime");
     
     /**
-     * Included resources.
-    public static final Scope INCLUDED = new DefaultScope("included", 
Collections.emptySet(), Collections.emptySet());
+     * Generic test dependencies. 
      */
+    public static final Scope TEST = new Scope("test");
     
-    static final class DefaultScope extends Scope {
-        private final Set<Scope> includes;
-        private final Set<Scope> stops;
-        private Set<Scope> implies;
-
-        public DefaultScope(String name, Set<Scope> includes, Set<Scope> 
stops) {
-            super(name);
-            this.includes = includes;
-            this.stops = stops;
-        }
-
-        @Override
-        public boolean includes(Scope s) {
-            return s == this || includes.contains(s);
-        }
-
-        @Override
-        public boolean exports(Scope s) {
-            return s == this || (!stops.contains(s) && includes(s));
-        }
-
-        @Override
-        public String toString() {
-            return name();
-        }
+    /**
+     * Dependencies directly declared by the project definition. Can be 
combined with other types to select just specific
+     * dependencies. Note that dependencies obtained using this modifier may 
be incomplete or version-unresolved, if they appear so
+     * in the build file. 
+     * <p>
+     * Note that it is not possible to add dependencies with this scope 
(exception will be thrown), it only serves as marker. Also no dependency
+     * will not be marked with this scope, all dependencies retain the scope 
they are declared for in the project's metadata.
+     */
+    public static final Scope DECLARED = new Scope("*declared");
 
-        @Override
-        public boolean implies(Scope s) {
-            return implies != null && implies.contains(s);
-        }
-        
-        public DefaultScope imply(Scope... scopes) {
-            this.implies = new HashSet<>(Arrays.asList(scopes));
-            return this;
-        }
-    }
+    /**
+     * Represents a plugin dependency. Plugin dependency extends applies a 
plugin to a build. PLUGIN dependency artifacts
+     * specify names and versions of those plugins. 
+     */
+    public static final Scope PLUGIN = new 
org.netbeans.modules.project.dependency.Scope("*plugin");
 }
diff --git a/java/gradle.java/nbproject/project.xml 
b/java/gradle.java/nbproject/project.xml
index e5f1c82670..80c664174f 100644
--- a/java/gradle.java/nbproject/project.xml
+++ b/java/gradle.java/nbproject/project.xml
@@ -376,6 +376,13 @@
                         
<code-name-base>org.netbeans.modules.project.dependency</code-name-base>
                         <compile-dependency/>
                     </test-dependency>
+                    <test-dependency>
+                        <!-- Because otherwise data systems and their services 
will not materialize -->
+                        
<code-name-base>org.netbeans.modules.settings</code-name-base>
+                    </test-dependency>
+                    <test-dependency>
+                        
<code-name-base>org.netbeans.modules.editor.mimelookup.impl</code-name-base>
+                    </test-dependency>
                 </test-type>
             </test-dependencies>
             <public-packages>
diff --git 
a/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/DependencyText.java
 
b/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/DependencyText.java
index 0f645397ba..6c18d5c208 100644
--- 
a/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/DependencyText.java
+++ 
b/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/DependencyText.java
@@ -28,7 +28,7 @@ import 
org.netbeans.modules.project.dependency.DependencyResult;
 /**
  * Single dependency information.
  */
-class DependencyText {
+public class DependencyText {
 
     /**
      * True if the dependency is a single statement, false if declared in
@@ -73,6 +73,10 @@ class DependencyText {
     String group;
     String name;
     String version;
+    
+    Style style;
+    
+    Container container;
 
     public DependencyText(String container, int startPos) {
         this.configuration = container;
@@ -118,11 +122,42 @@ class DependencyText {
             return sb.toString();
         }
     }
+    
+    public static enum Style {
+        /**
+         * Specified as attribtute: value list
+         */
+        MAP_LITERAL,
+        
+        /**
+         * Followed by {} customization block
+         */
+        CUSTOMIZED,
+        
+        /**
+         * Specified as single string,
+         */
+        GAV_STRING,
+        
+        /**
+         * Item in a multi-valued configuration container
+         */
+        CONTAINER_ITEM,
+        /**
+         * Surrounded by brackets
+         */
+        BRACKETS,
+        
+        /**
+         * Surrounded by parenthesis
+         */
+        PARENTHESIS
+    }
 
     /**
      * Dependency part information
      */
-    static final class Part {
+    public static final class Part {
 
         /**
          * Id of the part
@@ -156,9 +191,22 @@ class DependencyText {
         }
     }
     
+    public final static class Container {
+        final List<DependencyText> items;
+        final DependencyText.Part containerPart;
+
+        public Container(List<DependencyText> items, Part containerPart) {
+            this.items = items;
+            this.containerPart = containerPart;
+        }
+    }
+    
     
-    final static class Mapping {
+    public final static class Mapping {
         private final Map<Dependency, DependencyText> textMapping;
+        /**
+         * Container for all dependencies.
+         */
         private final DependencyText.Part container;
 
         public Mapping(Map<Dependency, DependencyText> textMapping, Part 
container) {
@@ -167,7 +215,7 @@ class DependencyText {
         }
 
         public DependencyText.Part getText(Dependency d, String part) {
-            if (DependencyResult.PART_CONTAINER.equals(part)) {
+            if (d == null && DependencyResult.PART_CONTAINER.equals(part)) {
                 return container;
             }
             DependencyText t = textMapping.get(d);
@@ -181,6 +229,13 @@ class DependencyText {
                 p.value = t.contents;
                 return p;
             }
+            if (DependencyResult.PART_CONTAINER.equals(part)) {
+                if (t.container != null) {
+                    return t.container.containerPart;
+                } else {
+                    return null;
+                }
+            }
             for (DependencyText.Part p : t.partList) {
                 if (part.equals(p.partId)) {
                     return p;
diff --git 
a/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/GradleDependenciesImplementation.java
 
b/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/GradleDependenciesImplementation.java
index bec452de03..49f09e8521 100644
--- 
a/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/GradleDependenciesImplementation.java
+++ 
b/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/GradleDependenciesImplementation.java
@@ -19,15 +19,18 @@
 package org.netbeans.modules.gradle.java.queries;
 
 import java.io.File;
+import java.io.IOException;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Queue;
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
@@ -52,6 +55,7 @@ import org.netbeans.spi.project.ProjectServiceProvider;
 import org.openide.filesystems.FileObject;
 import org.openide.filesystems.FileUtil;
 import org.openide.util.NbBundle;
+import org.openide.util.Pair;
 
 /**
  *
@@ -65,31 +69,44 @@ public class GradleDependenciesImplementation implements 
ProjectDependenciesImpl
     private final Project project;
     private final NbGradleProject nbgp;
     
-    private static final Set<Scope> SCOPES = new HashSet<>();
-
     public GradleDependenciesImplementation(Project project) {
         this.project = project;
         nbgp = NbGradleProject.get(project);
     }
     
-    static {
-        SCOPES.add(Scopes.PROCESS);
-        SCOPES.add(Scopes.COMPILE);
-        SCOPES.add(Scopes.RUNTIME);
-        SCOPES.add(Scopes.EXTERNAL);
-        SCOPES.add(Scopes.TEST);
-        SCOPES.add(Scopes.TEST_COMPILE);
-        SCOPES.add(Scopes.TEST_RUNTIME);
+    private GradleScopes scopes;
+    
+    String toGradleConfigName(Scope s) {
+        GradleScope gs = toGradleScope(s);
+        if (gs != null) {
+            return gs.getConfigurationName();
+        } else {
+            return null;
+        }
+    }
+    
+    GradleScopes gradleScopes() {
+        if (scopes == null) {
+            scopes = new GradleScopesBuilder(project).build();
+        }
+        return scopes;
     }
     
-    static final Map<Scope, String> SCOPE_TO_CONFIURATION = new HashMap<>();
+    Set<GradleScope> allScopes() {
+        return new HashSet<>(gradleScopes().scopes());
+    }
     
-    static {
-        SCOPE_TO_CONFIURATION.put(Scopes.PROCESS, "annotationProcessor");
-        SCOPE_TO_CONFIURATION.put(Scopes.COMPILE, "compileClasspath");
-        SCOPE_TO_CONFIURATION.put(Scopes.RUNTIME, "runtimeClasspath");
-        SCOPE_TO_CONFIURATION.put(Scopes.TEST_COMPILE, "testCompileClasspath");
-        SCOPE_TO_CONFIURATION.put(Scopes.TEST_RUNTIME, "testRuntimeClasspath");
+    GradleScope toGradleScope(Scope s) {
+        GradleScope gs = gradleScopes().toGradleScope(s);
+        if (gs != null) {
+            return gs;
+        }
+        String n = toGradleConfigName(s);
+        if (n != null) {
+            return gradleScopes().toGradleScope(n);
+        } else {
+            return null;
+        }
     }
 
     @NbBundle.Messages({
@@ -128,6 +145,8 @@ public class GradleDependenciesImplementation implements 
ProjectDependenciesImpl
     class Collector {
         final ProjectDependencies.DependencyQuery query;
         final GradleBaseProject base;
+        
+        boolean acceptUnresolved;
         GradleConfiguration cfg;
         Scope scope;
         List<Dependency> problems = new ArrayList<>();
@@ -149,35 +168,116 @@ public class GradleDependenciesImplementation implements 
ProjectDependenciesImpl
             base = GradleBaseProject.get(project);
         }
         
+        /**
+         * Creates a dependency result for project's declared dependencies. 
Since plugins may inject dependencies not present in the
+         * build file, the dependencies from the model are filtered for those 
present in the build file.
+         * 
+         * @param allScopes all scopes that should be included.
+         * @return dependency result instance
+         */
+        DependencyResult declaredDependencies(Set<Scope> allScopes) {
+            acceptUnresolved = true;
+            Set<String> cfgNames = new HashSet<>();
+            
+            List<Dependency> declared = new ArrayList<>();
+            
+            Set<? extends Scope> a;
+            if (allScopes.isEmpty()) {
+                a = allScopes();
+            } else {
+                a = allScopes;
+            }
+            
+            Set<GradleScope> all = allScopes();
+            
+            Queue<GradleScope> toProcess = new ArrayDeque<>();
+            for (Scope scope : a) {
+                GradleScope gs = gradleScopes().toGradleScope(scope);
+                if (gs != null) {
+                    toProcess.add(gs);
+                }
+            }
+            
+            GradleScope gs;
+            while ((gs = toProcess.poll()) != null) {
+                cfgNames.add(gs.getConfigurationName());
+                for (GradleScope t : all) {
+                    if (t.includes(gs)) {
+                        toProcess.add(t);
+                    }
+                }
+            }
+            
+            for (String cn : cfgNames) {
+                GradleConfiguration cfg = base.getConfigurations().get(cn);
+                if (cfg == null) {
+                    continue;
+                }
+                this.cfg = cfg;
+                this.scope = gradleScopes().toGradleScope(cn);
+                for (GradleDependency dep : cfg.getConfiguredDependencies()) {
+                    GradleConfiguration origin = cfg.getDependencyOrigin(dep);
+                    if (origin != null && origin != cfg) {
+                        // inherited
+                        continue;
+                    }
+                    declared.add(createDependency(dep, 
Collections.emptyList()));
+                }
+            }
+            File f = nbgp.getGradleFiles().getProjectDir();
+            FileObject pf = f == null ? null : FileUtil.toFileObject(f);
+            if (pf == null) {
+                throw new ProjectOperationException(project, 
ProjectOperationException.State.ERROR, Bundle.ERR_NoProjectDirectory(f));
+            }
+            ArtifactSpec part = createProjectArtifact(null, base.getPath(), 
null);
+            ProjectSpec pspec = ProjectSpec.create(base.getPath(), pf);
+            
+            Dependency tempRoot = Dependency.create(pspec, part, null, 
declared, project);
+            
+            NbGradleProject ngp = NbGradleProject.get(project);
+            GradleBaseProject gbp =  GradleBaseProject.get(project);
+            DependencyText.Mapping map;
+            try {
+                map = GradleDependencyResult.computeTextMappings(ngp, gbp, 
tempRoot.getChildren(), true);
+            } catch (IOException ex) {
+                throw new ProjectOperationException(project, 
ProjectOperationException.State.ERROR, "Unable to match dependencies to build 
script", ex);
+            }
+            for (Iterator<Dependency> it = declared.iterator(); it.hasNext(); 
) {
+                if (map.getText(it.next(), null) == null) {
+                    it.remove();
+                }
+            }
+            return new GradleDependencyResult(project, scopes, tempRoot);
+        }
+        
         DependencyResult processDependencies(NbGradleProject nbgp) {
             GradleBaseProject base =  GradleBaseProject.get(project);
-            Collection<Scope> scopes = query.getScopes();
+            Collection<Scope> userScopes = query.getScopes();
             
-            ArrayDeque<Scope> processScopes = new ArrayDeque<>(scopes);
-            Set<Scope> allScopes = new HashSet<>(scopes);
-            allScopes.retainAll(SCOPE_TO_CONFIURATION.keySet());
-            processScopes.removeAll(allScopes);
+            ArrayDeque<Scope> processScopes = new ArrayDeque<>(userScopes);
+            Set<Scope> allScopes = new HashSet<>();
             
+            if (processScopes.remove(Scopes.DECLARED)) {
+                return declaredDependencies(allScopes);
+            }
+
             // process unknown scopes
             while (!processScopes.isEmpty()) {
-                Scope s = processScopes.poll();
-                Set<Scope> newScopes = new HashSet<>();
-                newScopes.add(s);
-                for (Scope t : SCOPES) {
-                    if (s.includes(t)) {
-                        newScopes.add(t);
-                    } else if (t.implies(s)) {
-                        newScopes.add(t);
-                    }
+                Scope user = processScopes.poll();
+                GradleScope s = gradleScopes().toGradleScope(user);
+                if (!s.getConfigurationName().equals(s.name())) {
+                    Set<Scope> newScopes = new HashSet<>();
+                    newScopes.addAll(s.getIncluded());
+                    newScopes.removeAll(allScopes);
+                    processScopes.addAll(newScopes);
+                } else {
+                    allScopes.add(s);
                 }
-                newScopes.removeAll(allScopes);
-                allScopes.addAll(newScopes);
-                processScopes.addAll(newScopes);
             }
             
             List<Dependency> rootDeps = new ArrayList<>();
             for (Scope s : allScopes) {
-                String cfgName = SCOPE_TO_CONFIURATION.get(s);
+                String cfgName = toGradleConfigName(s);
                 if (cfgName == null) {
                     continue;
                 }
@@ -198,6 +298,7 @@ public class GradleDependenciesImplementation implements 
ProjectDependenciesImpl
                         // safeguard: we cannot determine the origin, so let's 
assume this configuration defines the dependency
                         this.cfg = cfg;
                     }
+                    this.scope = 
gradleScopes().toGradleScope(this.cfg.getName());
                     List<Dependency> ch = processLevel(cfg, dep, new 
LinkedHashSet<>());
                     Dependency n = createDependency(dep, ch);
                     rootDeps.add(n);
@@ -214,7 +315,7 @@ public class GradleDependenciesImplementation implements 
ProjectDependenciesImpl
             
             Dependency root = Dependency.create(pspec, part, null, rootDeps, 
project);
             
-            return new GradleDependencyResult(project, root);
+            return new GradleDependencyResult(project, scopes, root);
         }
         
         ArtifactSpec createProjectArtifact(GradleDependencyResult.Info info, 
String projectId, List<Dependency> children) {
@@ -245,7 +346,23 @@ public class GradleDependenciesImplementation implements 
ProjectDependenciesImpl
         Dependency createDependency(GradleDependency dep, List<Dependency> 
children) {
             GradleDependencyResult.Info info = new 
GradleDependencyResult.Info(cfg, dep);
             if (dep instanceof GradleDependency.UnresolvedDependency) {
-                return null;
+                if (acceptUnresolved) {
+                    GradleDependency.UnresolvedDependency gud = 
(GradleDependency.UnresolvedDependency)dep;
+                    String[] gav = gud.getId().split(":");
+                    if (gav.length < 2) {
+                        // not group:artifact, cannot represent as an artifact
+                        return null;
+                    }
+                    ArtifactSpec spec = 
+                            ArtifactSpec.createVersionSpec(gav[0], gav[1], 
+                                    null, 
+                                    gav.length >= 4 ? gav[3] : null,
+
+                                    gav.length >= 3 ? gav[2] : null, false, 
null, dep);
+                    return Dependency.create(spec, scope, children, info);
+                } else {
+                    return null;
+                }
             }            
             
             if (dep instanceof GradleDependency.ProjectDependency) {
diff --git 
a/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/GradleDependencyResult.java
 
b/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/GradleDependencyResult.java
index 25cffdd7de..81850909e3 100644
--- 
a/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/GradleDependencyResult.java
+++ 
b/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/GradleDependencyResult.java
@@ -42,6 +42,7 @@ import org.netbeans.modules.gradle.api.NbGradleProject;
 import org.netbeans.modules.project.dependency.ArtifactSpec;
 import org.netbeans.modules.project.dependency.Dependency;
 import org.netbeans.modules.project.dependency.DependencyResult;
+import org.netbeans.modules.project.dependency.ProjectScopes;
 import org.netbeans.modules.project.dependency.SourceLocation;
 import org.openide.cookies.EditorCookie;
 import org.openide.filesystems.FileObject;
@@ -59,6 +60,7 @@ public final class GradleDependencyResult implements 
DependencyResult, PropertyC
     private final Project gradleProject;
     private final Dependency root;
     private boolean valid = true;
+    private final GradleScopes scopes;
 
     // @GuardedBy(this)
     PropertyChangeListener wL;
@@ -73,8 +75,9 @@ public final class GradleDependencyResult implements 
DependencyResult, PropertyC
     
     private final FileObject projectFile;
 
-    public GradleDependencyResult(Project gradleProject, Dependency root) {
+    public GradleDependencyResult(Project gradleProject, GradleScopes scopes, 
Dependency root) {
         this.gradleProject = gradleProject;
+        this.scopes = scopes;
         this.root = root;
         this.gp = NbGradleProject.get(gradleProject);
         File bs = gp.getGradleFiles().getBuildScript();
@@ -96,6 +99,11 @@ public final class GradleDependencyResult implements 
DependencyResult, PropertyC
         return valid;
     }
 
+    @Override
+    public ProjectScopes getScopes() {
+        return scopes;
+    }
+
     @Override
     public Collection<ArtifactSpec> getProblemArtifacts() {
         return Collections.unmodifiableCollection(problems);
@@ -149,24 +157,12 @@ public final class GradleDependencyResult implements 
DependencyResult, PropertyC
 
     // @GuardedBy(this)
     private DependencyText.Mapping  sourceMapping;
-
-    @Override
-    public SourceLocation getDeclarationRange(Dependency d, String part) 
throws IOException {
-        DependencyText.Mapping  mapping;
-        DependencyText.Part p = null;
-        
-        synchronized (this) {
-            mapping = sourceMapping;
-            if (mapping != null) {
-                return getDeclarationRange0(mapping, d, part);
-            }
-        }
-        
-        GradleBaseProject gbp = GradleBaseProject.get(gradleProject);
+    
+    static DependencyText.Mapping computeTextMappings(NbGradleProject gp, 
GradleBaseProject gbp, List<Dependency> children, boolean matchScopes) throws 
IOException {
         Collection<String> cfgNames = gbp.getConfigurations().keySet();
-        TextDependencyScanner ts = new 
TextDependencyScanner().withConfigurations(cfgNames);
+        TextDependencyScanner ts = new 
TextDependencyScanner(matchScopes).withConfigurations(cfgNames);
         
-        root.getChildren().forEach(rd -> {
+        children.forEach(rd -> {
             Info info = (Info)rd.getProjectData();
             GradleConfiguration org = 
info.config.getDependencyOrigin(info.gradleDependency);
             if (org == null) {
@@ -174,8 +170,9 @@ public final class GradleDependencyResult implements 
DependencyResult, PropertyC
             }
             ts.addDependencyOrigin(info.gradleDependency, org.getName());
         });
-        
+
         String contents = null;
+        FileObject projectFile = 
FileUtil.toFileObject(gp.getGradleFiles().getBuildScript());
         if (projectFile != null) {
             EditorCookie cake = null;
             cake = projectFile.getLookup().lookup(EditorCookie.class);
@@ -198,8 +195,23 @@ public final class GradleDependencyResult implements 
DependencyResult, PropertyC
         }
         ts.parseDependencyList(contents);
         
-        mapping = ts.mapDependencies(root.getChildren());
+        return ts.mapDependencies(children);
+    }
+
+    @Override
+    public SourceLocation getDeclarationRange(Dependency d, String part) 
throws IOException {
+        DependencyText.Mapping  mapping;
+        DependencyText.Part p = null;
         
+        synchronized (this) {
+            mapping = sourceMapping;
+            if (mapping != null) {
+                return getDeclarationRange0(mapping, d, part);
+            }
+        }
+        
+        mapping = computeTextMappings(gp, 
GradleBaseProject.get(gradleProject), root.getChildren(), false);
+
         synchronized (this) {
             if (sourceMapping == null) {
                 sourceMapping = mapping;
@@ -210,10 +222,13 @@ public final class GradleDependencyResult implements 
DependencyResult, PropertyC
     
     private SourceLocation getDeclarationRange0(DependencyText.Mapping 
mapping, Dependency d, String part) {
         Dependency direct = d;
-        while (direct.getParent() != null && direct.getParent() != root) {
-            direct = direct.getParent();
+        // there's the special case with CONTAINER part for all the 
dependencies.
+        if (d != null) {
+            while (direct.getParent() != null && direct.getParent() != root) {
+                direct = direct.getParent();
+            }
         }
-        DependencyText.Part found = mapping.getText(direct, part);
+        DependencyText.Part found = mapping.getText(direct == root ? null : 
direct, part);
         if (found == null) {
             return null;
         }
diff --git 
a/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/GradleScope.java
 
b/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/GradleScope.java
new file mode 100644
index 0000000000..191d720204
--- /dev/null
+++ 
b/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/GradleScope.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.gradle.java.queries;
+
+import java.util.Collection;
+import java.util.HashSet;
+import org.netbeans.modules.project.dependency.Scope;
+
+/**
+ * Custom gradle scopes that copy the structure of Gradle's standard 
configurations.
+ * Each scope must point to a configuration where the artifacts for the scope 
will be added.
+ * 
+ * @author sdedic
+ */
+public final class GradleScope extends Scope {
+    private final String configurationName;
+    private final String targetConfiguration;
+    private Collection<Scope> includes;
+    private Collection<Scope> implies;
+    
+    GradleScope(String name, Collection<Scope> includes, Collection<Scope> 
implies) {
+        this(name, name, name, includes, implies);
+    }
+
+    GradleScope(String name, String cfgName, String modifyCfgName, 
Collection<Scope> includes, Collection<Scope> implies) {
+        super(name); // NOI18N
+        this.configurationName = cfgName;
+        this.targetConfiguration = modifyCfgName;
+        this.includes = includes == null ? new HashSet<>() : includes;
+        this.implies = implies == null ? new HashSet<>() : implies;
+    }
+    
+    public boolean includes(Scope s) {
+        return includes.contains(s.name());
+    }
+    
+    public Collection<? extends Scope> getIncluded() {
+        return includes;
+    }
+    
+    public Collection<? extends Scope> getInheritedInto() {
+        return implies;
+    }
+
+    public boolean implies(Scope s) {
+        return implies.contains(s.name());
+    }
+    
+    public String getConfigurationName() {
+        return configurationName;
+    }
+
+    public String getTargetConfigurationName() {
+        return targetConfiguration;
+    }
+    
+    public String toString() {
+        return name();
+    }
+    
+    /**
+     * Name of the "implementation" scope
+     */
+    static final String IMPLEMENTATION = "implementation";
+}
diff --git 
a/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/GradleScopes.java
 
b/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/GradleScopes.java
new file mode 100644
index 0000000000..203e5f9515
--- /dev/null
+++ 
b/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/GradleScopes.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.gradle.java.queries;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import org.netbeans.api.project.Project;
+import org.netbeans.modules.project.dependency.ProjectScopes;
+import org.netbeans.modules.project.dependency.Scope;
+
+/**
+ * Implementation of ProjectScopes that bridges Gradle configutations.
+ * 
+ * @author sdedic
+ */
+public final class GradleScopes implements ProjectScopes{
+    private final Project   project;
+    private final Map<String, GradleScope> scopes;
+
+    public GradleScopes(Project project, Map<String, GradleScope> scopes) {
+        this.project = project;
+        this.scopes = Collections.unmodifiableMap(scopes);
+    }
+
+    @Override
+    public Collection<GradleScope> scopes() {
+        return scopes.values();
+    }
+    
+    public GradleScope toGradleScope(String n) {
+        return scopes.get(n);
+    }
+    
+    public GradleScope toGradleScope(Scope s) {
+        return scopes.getOrDefault(s.name(), 
scopes.get(GradleScope.IMPLEMENTATION));
+    }
+
+    @Override
+    public Collection<? extends Scope> implies(Scope s, boolean direct) {
+        GradleScope scope = scopes.get(s.name());
+        if (scope == null) {
+            return Collections.emptyList();
+        }
+        if (direct) {
+            return scope.getInheritedInto();
+        }
+        return Collections.emptySet();
+    }
+}
diff --git 
a/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/GradleScopesBuilder.java
 
b/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/GradleScopesBuilder.java
new file mode 100644
index 0000000000..8cc6ae70e9
--- /dev/null
+++ 
b/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/GradleScopesBuilder.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.gradle.java.queries;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import org.netbeans.api.project.Project;
+import org.netbeans.modules.gradle.api.GradleBaseProject;
+import org.netbeans.modules.gradle.api.GradleConfiguration;
+import org.netbeans.modules.project.dependency.Scope;
+import org.netbeans.modules.project.dependency.Scopes;
+
+/**
+ * Builds scopes for the specific project, using its configurations.
+ * @author sdedic
+ */
+public final class GradleScopesBuilder {
+    private final static Map<String, String> REMAP_ABSTRACT_SCOPES = new 
HashMap<>();
+    private final Project project;
+    private final GradleBaseProject gbp;
+
+    public GradleScopesBuilder(Project project) {
+        this.project = project;
+        this.gbp = GradleBaseProject.get(project);
+    }
+    
+    private Map<String, Collection<String>> extendsFrom = new HashMap<>();
+    private Map<String, Collection<String>> inheritedInto = new HashMap<>();
+    
+    static {
+        REMAP_ABSTRACT_SCOPES.put(Scopes.COMPILE.name(), "compileClasspath");
+        REMAP_ABSTRACT_SCOPES.put(Scopes.RUNTIME.name(), "runtimeClasspath");
+        REMAP_ABSTRACT_SCOPES.put(Scopes.TEST_COMPILE.name(), 
"testCompileClasspath");
+        REMAP_ABSTRACT_SCOPES.put(Scopes.TEST_RUNTIME.name(), 
"testRuntimeClasspath");
+        REMAP_ABSTRACT_SCOPES.put(Scopes.TEST.name(), 
Scopes.TEST_RUNTIME.name() + "," + Scopes.TEST_COMPILE.name() + "," + 
"testRuntimeClasspath,testCompileClasspath");
+    }
+    
+    private void addToMap(Map<String, Collection<String>> map, String k, 
String c) {
+        map.computeIfAbsent(k, n -> new LinkedHashSet<>(2)).add(c);
+    }
+    
+    private void addDependency(String inheritedTo, String inheritedFrom) {
+        addToMap(extendsFrom, inheritedTo, inheritedFrom);
+        addToMap(inheritedInto, inheritedFrom, inheritedTo);
+    }
+    
+    Map<String, GradleScope> scopes = new HashMap<>();
+    
+    private GradleScope createMetaScope(String metaName, String config, String 
targetConfig) {
+        GS gs = new GS();
+        scopeData.put(metaName, gs);
+        addDependency(metaName, config);
+        GradleScope s = new GradleScope(metaName, config, targetConfig, 
gs.extendsFrom, gs.inheritedInto);
+        scopes.put(metaName, s);
+        return s;
+    }
+    
+    static class GS {
+        Set<Scope> extendsFrom = new HashSet<>();
+        Set<Scope> inheritedInto = new HashSet<>();
+    }
+    
+    private Map<String, GS> scopeData = new HashMap<>();
+    
+    public GradleScopes build() {
+        for (String cfg : gbp.getConfigurations().keySet()) {
+            GradleConfiguration c = gbp.getConfigurations().get(cfg);
+            c.getExtendsFrom().forEach(p -> {
+                addDependency(cfg, p.getName());
+            });
+        }
+        
+        addDependency(Scopes.EXTERNAL.name(), "compileOnly");
+        addDependency(Scopes.PROCESS.name(), "annotationProcessor");
+        addDependency(Scopes.COMPILE.name(), "compileClasspath");
+        addDependency(Scopes.RUNTIME.name(), "runtimeClasspath");
+        addDependency(Scopes.TEST_COMPILE.name(), "testCompileClasspath");
+        addDependency(Scopes.TEST_RUNTIME.name(), "testRuntimeClasspath");
+        addDependency(Scopes.TEST.name(), Scopes.TEST_RUNTIME.name() + "," + 
Scopes.TEST_COMPILE.name());
+        
+        for (String cfg : gbp.getConfigurations().keySet()) {
+            GS gs = new GS();
+            scopeData.put(cfg, gs);
+            scopes.put(cfg, new GradleScope(cfg, gs.extendsFrom, 
gs.inheritedInto));
+        }
+        
+        createMetaScope(Scopes.EXTERNAL.name(), "compileOnly", "compileOnly");
+        createMetaScope(Scopes.PROCESS.name(), "annotationProcessor", 
"annotationProcessor");
+        createMetaScope(Scopes.COMPILE.name(), "compileClasspath", 
"implementation");
+        createMetaScope(Scopes.RUNTIME.name(), "runtimeClasspath", 
"runtimeOnly");
+        createMetaScope(Scopes.TEST_COMPILE.name(), "testCompileClasspath", 
"testCompileClasspath");
+        createMetaScope(Scopes.TEST_RUNTIME.name(), "testRuntimeClasspath", 
"testRuntimeClasspath");
+        createMetaScope(Scopes.TEST.name(), "testCompileClasspath", 
"testImplementation");
+        
+        for (GradleScope gs : scopes.values()) {
+            GS data = scopeData.get(gs.name());
+            
+            extendsFrom.getOrDefault(gs.name(), Collections.emptyList()).
+                    stream().
+                    map(scopes::get).forEach(data.extendsFrom::add);
+            inheritedInto.getOrDefault(gs.name(), Collections.emptyList()).
+                    stream().
+                    map(scopes::get).forEach(data.inheritedInto::add);
+        }
+
+        return new GradleScopes(project, scopes);
+    }
+}
diff --git 
a/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/TextDependencyScanner.java
 
b/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/TextDependencyScanner.java
index d7feffb11e..cab60320a2 100644
--- 
a/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/TextDependencyScanner.java
+++ 
b/java/gradle.java/src/org/netbeans/modules/gradle/java/queries/TextDependencyScanner.java
@@ -41,6 +41,7 @@ public class TextDependencyScanner {
     
     private final Map<GradleDependency, String> origins = new HashMap<>();
     private final Map<GradleDependency, Map<String, SourceLocation>> locations 
= new HashMap<>();
+    private final boolean matchScopes;
     
     private List<DependencyText> dependencies = new ArrayList<>();
     
@@ -93,7 +94,10 @@ public class TextDependencyScanner {
      * read.
      */
     int newLinePos;
-    
+
+    public TextDependencyScanner(boolean matchScopes) {
+        this.matchScopes = matchScopes;
+    }
     
     /**
      * End of the last item in the group
@@ -522,6 +526,7 @@ public class TextDependencyScanner {
         while ((c = nextToken()) != '}') {
             if ((!onlyAfterNewline || wasEndOfLine()) && (tokenType == 
Token.IDENTIFIER)) {
                 if (configurationNames.contains(tokenText)) {
+                    String saveToken = tokenText;
                     groupStartPos = tokenStart;
                     groupItemCount = 0;
                     scanDepdendencyContainer(tokenText);
@@ -532,6 +537,15 @@ public class TextDependencyScanner {
                         if (groupItemsEnd != -1) {
                             last.endPos = groupItemsEnd;
                         }
+                    } else {
+                        DependencyText.Part containerPart = new 
DependencyText.Part();
+                        containerPart.partId = saveToken;
+                        containerPart.startPos = groupStartPos;
+                        containerPart.endPos = groupItemsEnd;
+                        containerPart.quoted = 0;
+                        List<DependencyText> items = new 
ArrayList<>(dependencies.subList(dependencies.size() - groupItemCount, 
dependencies.size()));
+                        DependencyText.Container nc = new 
DependencyText.Container(items, containerPart);
+                        items.forEach(i -> i.container = nc);
                     }
                     onlyAfterNewline = false;
                     continue;
@@ -559,6 +573,7 @@ public class TextDependencyScanner {
         try {
             findDependencyBlock();
             buildDependencies();
+            dependencyBlockEnd = pos;
             computeGAV();
         } catch (EndInputException ex) {
             // no op, just terminate processing
@@ -583,7 +598,7 @@ public class TextDependencyScanner {
         }
     }
     
-    private int dependencyBlockStart;
+    private int dependencyBlockStart = -1;
     private int dependencyBlockEnd;
     
     private void findDependencyBlock() {
@@ -596,7 +611,6 @@ public class TextDependencyScanner {
                     c = skipWhitespace();
                     if (c == '{') {
                         nextChar();
-                        dependencyBlockEnd = pos;
                         return;
                     }
                 }
@@ -638,6 +652,16 @@ public class TextDependencyScanner {
         }
     }
     
+    private boolean scopeMatches(Dependency d, DependencyText t) {
+        if (!matchScopes) {
+            return true;
+        }
+        if (d.getScope() == null || t.configuration == null) {
+            return true;
+        }
+        return d.getScope().name().equals(t.configuration);
+    }
+    
     private DependencyText findDependency(Dependency d) {
         String projectName = null;
         String gav = null;
@@ -661,13 +685,13 @@ public class TextDependencyScanner {
             if (DependencyText.KEYWORD_PROJECT.equals(t.keyword) &&
                 t.contents.equals(projectName)) {
                 return t;
-            } else if (t.keyword == null && t.getContentsOrGav().equals(gav)) {
+            } else if (t.keyword == null && t.getContentsOrGav().equals(gav) 
&& scopeMatches(d, t)) {
                 return t;
             }
         }
         
         for (DependencyText t : dependencies) {
-            if (t.keyword == null && t.contents != null && 
t.contents.equals(groupAndName)) {
+            if (t.keyword == null && t.contents != null && 
t.contents.equals(groupAndName) && scopeMatches(d, t)) {
                 return t;
             }
         }
@@ -685,11 +709,17 @@ public class TextDependencyScanner {
             }
         }
         
-        DependencyText.Part containerPart = new DependencyText.Part();
-        containerPart.partId = DependencyResult.PART_CONTAINER;
-        containerPart.startPos = dependencyBlockStart;
-        containerPart.endPos = dependencyBlockEnd;
-        containerPart.value = "";
+        DependencyText.Part containerPart;
+        
+        if (dependencyBlockStart != -1) {
+            containerPart = new DependencyText.Part();
+            containerPart.partId = DependencyResult.PART_CONTAINER;
+            containerPart.startPos = dependencyBlockStart;
+            containerPart.endPos = dependencyBlockEnd;
+            containerPart.value = "";
+        } else {
+            containerPart = null;
+        }
         
         return new DependencyText.Mapping(result, containerPart);
     }
diff --git 
a/java/gradle.java/test/unit/data/dependencies/micronaut/build.gradle 
b/java/gradle.java/test/unit/data/dependencies/micronaut/build.gradle
index 3ce34ee2b6..ae9934a2a8 100644
--- a/java/gradle.java/test/unit/data/dependencies/micronaut/build.gradle
+++ b/java/gradle.java/test/unit/data/dependencies/micronaut/build.gradle
@@ -32,12 +32,16 @@ repositories {
 dependencies {
     annotationProcessor("io.micronaut:micronaut-http-validation")
     implementation("io.micronaut:micronaut-http-client")
-    implementation("io.micronaut:micronaut-jackson-databind")
+    implementation(
+            "io.micronaut:micronaut-jackson-databind"
+    )
     implementation("jakarta.annotation:jakarta.annotation-api")
     runtimeOnly("ch.qos.logback:logback-classic")
-    implementation("io.micronaut:micronaut-validation")
 
-    implementation("org.apache.logging.log4j:log4j-core:2.17.0")
+    implementation(
+        "io.micronaut:micronaut-validation",
+        "org.apache.logging.log4j:log4j-core:2.17.0"
+    )
 }
 
 
diff --git 
a/java/gradle.java/test/unit/data/dependencies/parse/variousSyntax.gradle 
b/java/gradle.java/test/unit/data/dependencies/parse/variousSyntax.gradle
index d3fb44b5b3..b56a21e06c 100644
--- a/java/gradle.java/test/unit/data/dependencies/parse/variousSyntax.gradle
+++ b/java/gradle.java/test/unit/data/dependencies/parse/variousSyntax.gradle
@@ -7,16 +7,16 @@ group = "com.example"
 repositories {
     mavenCentral()
 }
-dependencies {
+@@T@@dependencies {
     // with parenthesis
     @@A@@annotationProcessor("io.micronaut:micronaut-http-validation")@@A@@
     // without parenthesis
     @@B@@implementation "io.micronaut:micronaut-http-client"@@B@@
     // several deps in a common group
-    implementation(
+    @@P@@implementation(
         @@C@@'io.micronaut:micronaut-jackson-databind'@@C@@,
         @@D@@"jakarta.annotation:jakarta.annotation-api"@@D@@
-    )
+    )@@Q@@
     // with a closure
     @@E@@runtimeOnly("ch.qos.logback:logback-classic") {
         transitive = true
@@ -24,12 +24,12 @@ dependencies {
     // map in parenthesis
     @@F@@implementation(group : "io.micronaut", name: "micronaut-validation", 
version: "2.5")@@F@@
     // list of maps in parenthesis
-    runtimeOnly(
+    @@R@@runtimeOnly(
         @@G@@[group: 'org.hibernate', name: 'hibernate', version: '3.0.5', 
transitive: true]@@G@@,
         @@H@@[group:'org.ow2.asm', name:'asm', version:'7.1']@@H@@
-    )
+    )@@S@@
     @@I@@implementation group: 'org.apache.logging.log4j', name: 'log4j-core', 
version: '2.17.0'@@I@@
-}
+}@@U@@
 application {
     mainClass.set("com.example.Application")
 }
diff --git 
a/java/gradle.java/test/unit/src/org/netbeans/modules/gradle/java/queries/GradleDependenciesImplementationTest.java
 
b/java/gradle.java/test/unit/src/org/netbeans/modules/gradle/java/queries/GradleDependenciesImplementationTest.java
index 82d5f3d184..db4ec94d38 100644
--- 
a/java/gradle.java/test/unit/src/org/netbeans/modules/gradle/java/queries/GradleDependenciesImplementationTest.java
+++ 
b/java/gradle.java/test/unit/src/org/netbeans/modules/gradle/java/queries/GradleDependenciesImplementationTest.java
@@ -19,10 +19,13 @@
 package org.netbeans.modules.gradle.java.queries;
 
 import java.io.File;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Optional;
 import java.util.stream.Collectors;
 import static junit.framework.TestCase.assertNotNull;
+import org.netbeans.api.editor.document.LineDocument;
+import org.netbeans.api.editor.document.LineDocumentUtils;
 import org.netbeans.api.project.Project;
 import org.netbeans.api.project.ProjectManager;
 import org.netbeans.api.project.ui.OpenProjects;
@@ -37,6 +40,7 @@ import 
org.netbeans.modules.project.dependency.DependencyResult;
 import org.netbeans.modules.project.dependency.ProjectDependencies;
 import org.netbeans.modules.project.dependency.Scopes;
 import org.netbeans.modules.project.dependency.SourceLocation;
+import org.openide.cookies.EditorCookie;
 import org.openide.filesystems.FileObject;
 import org.openide.filesystems.FileUtil;
 import org.openide.modules.DummyInstalledFileLocator;
@@ -133,6 +137,84 @@ public class GradleDependenciesImplementationTest extends 
NbTestCase {
         assertSame("Project is passed as project data - internal", p, 
r.getRoot().getProjectData());
     }
     
+    public void testDependencyNoDependencies() throws Exception {
+        Project p = makeProject("dependencies/simple2");
+        DependencyResult r = ProjectDependencies.findDependencies(p, 
+            ProjectDependencies.newQuery(Scopes.RUNTIME)
+        );
+        assertNotNull("Dependency service is supported", r);
+        SourceLocation loc = r.getDeclarationRange(null, 
DependencyResult.PART_CONTAINER);
+        assertNull(loc);
+        
+        loc = r.getDeclarationRange(r.getRoot(), 
DependencyResult.PART_CONTAINER);
+        assertNull(loc);
+    }
+    
+    public void testDependencyBlockRange() throws Exception {
+        Project p = makeProject("dependencies/micronaut");
+        DependencyResult r = ProjectDependencies.findDependencies(p, 
+            ProjectDependencies.newQuery(Scopes.RUNTIME)
+        );
+        assertNotNull("Dependency service is supported", r);
+        
+        SourceLocation loc = r.getDeclarationRange(null, 
DependencyResult.PART_CONTAINER);
+        assertNotNull(loc);
+        
+        loc = r.getDeclarationRange(r.getRoot(), 
DependencyResult.PART_CONTAINER);
+        assertNotNull(loc);
+      
+        FileObject buildGradle = projectDir.getFileObject("build.gradle");
+        EditorCookie cake = buildGradle.getLookup().lookup(EditorCookie.class);
+        LineDocument doc = LineDocumentUtils.asRequired(cake.openDocument(), 
LineDocument.class);
+        
+        int start = loc.getStartOffset();
+        int depStart = LineDocumentUtils.getLineFirstNonWhitespace(doc, start);
+        int depEnd = LineDocumentUtils.getLineEnd(doc, start);
+        
+        String s = doc.getText(depStart, depEnd - depStart);
+        assertEquals("dependencies {", s);
+        
+        int end = loc.getEndOffset();
+        s = doc.getText(end - 1, 1);
+        assertEquals("}", s);
+    }
+    
+    public void testLocationOfBlockDependencyList() throws Exception {
+        Project p = makeProject("dependencies/micronaut");
+        DependencyResult r = ProjectDependencies.findDependencies(p, 
+            ProjectDependencies.newQuery(Scopes.RUNTIME)
+        );
+        assertNotNull("Dependency service is supported", r);
+        
+        Dependency pin1 = r.getRoot().getChildren().stream().filter(d -> 
d.toString().contains("io.micronaut:micronaut-validation")).findAny().get();
+        Dependency pin2 = r.getRoot().getChildren().stream().filter(d -> 
d.toString().contains("ch.qos.logback:logback-classic")).findAny().get();
+        Dependency pin3 = r.getRoot().getChildren().stream().filter(d -> 
d.toString().contains("io.micronaut:micronaut-jackson-databind")).findAny().get();
+        
+        SourceLocation loc = r.getDeclarationRange(pin2, 
DependencyResult.PART_CONTAINER);
+        assertNull(loc);
+        
+        loc = r.getDeclarationRange(pin3, DependencyResult.PART_CONTAINER);
+        assertNull(loc);
+        
+        loc = r.getDeclarationRange(pin1, DependencyResult.PART_CONTAINER);
+        assertNotNull(loc);
+      
+        FileObject buildGradle = projectDir.getFileObject("build.gradle");
+        EditorCookie cake = buildGradle.getLookup().lookup(EditorCookie.class);
+        LineDocument doc = LineDocumentUtils.asRequired(cake.openDocument(), 
LineDocument.class);
+        
+        int start = loc.getStartOffset();
+        int depStart = LineDocumentUtils.getLineFirstNonWhitespace(doc, start);
+        int depEnd = LineDocumentUtils.getLineEnd(doc, start);
+        
+        String s = doc.getText(depStart, depEnd - depStart);
+        assertEquals("implementation(", s);
+        
+        int end = loc.getEndOffset();
+        s = doc.getText(end - 1, 1);
+        assertEquals(")", s);
+    }
+    
     public void testMicronautProject() throws Exception {
         Project p = makeProject("dependencies/micronaut");
         DependencyResult r = ProjectDependencies.findDependencies(p, 
@@ -174,5 +256,44 @@ public class GradleDependenciesImplementationTest extends 
NbTestCase {
         }
         assertNotNull("Implied dependency should have a root dep", rd);
         assertSame(rd, srcLoc.getImpliedBy());
-  }
+    }
+    
+    private void assertContainsDependency(List<Dependency> deps, String 
groupAndArtifact) {
+        for (Dependency d : deps) {
+            ArtifactSpec a = d.getArtifact();
+            if (a != null) {
+                String ga = a.getGroupId() + ":" + a.getArtifactId();
+                if (groupAndArtifact.equals(ga)) {
+                    return;
+                }
+            }
+        }
+        fail("Artifact not found: " +  groupAndArtifact);
+    }
+    
+    private static final List<String> ALL_DEPS =   Arrays.asList(
+            "io.micronaut:micronaut-http-validation",
+            "io.micronaut:micronaut-http-client",
+            "io.micronaut:micronaut-jackson-databind",
+            "jakarta.annotation:jakarta.annotation-api",
+            "ch.qos.logback:logback-classic",
+            "io.micronaut:micronaut-validation",
+            "org.apache.logging.log4j:log4j-core"
+    );
+    
+    public void testMicronautProjectDeclaredDependencies() throws Exception {
+        Project p = makeProject("dependencies/micronaut");
+ 
+        DependencyResult r = ProjectDependencies.findDependencies(p, 
+            ProjectDependencies.newQuery(Scopes.DECLARED)
+        );
+        assertNotNull("Dependency service is supported", r);
+        
+        List<Dependency> deps = r.getRoot().getChildren();
+        for (String d : ALL_DEPS) {
+            assertContainsDependency(deps, d);
+        }
+        
+        assertTrue("Contains versioned log4j dependency", 
deps.stream().filter(d -> 
d.getArtifact().toString().contains(".log4j:log4j-core:2.17.0")).findAny().isPresent());
+    }
 }
diff --git 
a/java/gradle.java/test/unit/src/org/netbeans/modules/gradle/java/queries/RegexpGradleScannerTest.java
 
b/java/gradle.java/test/unit/src/org/netbeans/modules/gradle/java/queries/RegexpGradleScannerTest.java
index ace9d9d80b..d30297ab66 100644
--- 
a/java/gradle.java/test/unit/src/org/netbeans/modules/gradle/java/queries/RegexpGradleScannerTest.java
+++ 
b/java/gradle.java/test/unit/src/org/netbeans/modules/gradle/java/queries/RegexpGradleScannerTest.java
@@ -31,6 +31,7 @@ import org.netbeans.junit.NbTestCase;
 import org.netbeans.modules.project.dependency.ArtifactSpec;
 import org.netbeans.modules.project.dependency.Dependency;
 import org.netbeans.modules.project.dependency.ProjectSpec;
+import org.netbeans.modules.project.dependency.Scope;
 import org.netbeans.modules.project.dependency.Scopes;
 import org.openide.filesystems.FileObject;
 import org.openide.filesystems.FileUtil;
@@ -56,7 +57,7 @@ public class RegexpGradleScannerTest extends NbTestCase {
      */
     public void testComplexPrologue() throws Exception {
         FileObject f = 
FileUtil.toFileObject(getDataDir()).getFileObject("dependencies/parse/complexPrologue.gradle");
-        TextDependencyScanner scanner = new TextDependencyScanner();
+        TextDependencyScanner scanner = new TextDependencyScanner(true);
         
         scanner.withConfigurations(Arrays.asList(
             "runtimeOnly", "implementation"
@@ -68,7 +69,7 @@ public class RegexpGradleScannerTest extends NbTestCase {
     
     public void testSkipExecutableCodeInDependencies() throws Exception {
         FileObject f = 
FileUtil.toFileObject(getDataDir()).getFileObject("dependencies/parse/executableCodeInDependencies.gradle");
-        TextDependencyScanner scanner = new TextDependencyScanner();
+        TextDependencyScanner scanner = new TextDependencyScanner(true);
         
         scanner.withConfigurations(Arrays.asList(
             "runtimeOnly", "implementation"
@@ -81,7 +82,7 @@ public class RegexpGradleScannerTest extends NbTestCase {
     
     public void testScanSimpleScript() throws Exception {
         FileObject f = 
FileUtil.toFileObject(getDataDir()).getFileObject("dependencies/parse/simple.gradle");
-        TextDependencyScanner scanner = new TextDependencyScanner();
+        TextDependencyScanner scanner = new TextDependencyScanner(true);
         
         scanner.withConfigurations(Arrays.asList(
             "runtimeOnly", "implementation"
@@ -96,7 +97,7 @@ public class RegexpGradleScannerTest extends NbTestCase {
     
     public void testMicronautStarter() throws Exception {
         FileObject f = 
FileUtil.toFileObject(getDataDir()).getFileObject("dependencies/parse/starter.gradle");
-        TextDependencyScanner scanner = new TextDependencyScanner();
+        TextDependencyScanner scanner = new TextDependencyScanner(true);
         
         scanner.withConfigurations(Arrays.asList(
             "runtimeOnly", "implementation", "annotationProcessor"
@@ -124,7 +125,7 @@ public class RegexpGradleScannerTest extends NbTestCase {
      */
     public void testVariousSyntaxes() throws Exception {
         FileObject f = 
FileUtil.toFileObject(getDataDir()).getFileObject("dependencies/parse/variousSyntax.gradle");
-        TextDependencyScanner scanner = new TextDependencyScanner();
+        TextDependencyScanner scanner = new TextDependencyScanner(true);
         
         filteredText = filterAndStorePositions(f.asText());
         scanner.withConfigurations(Arrays.asList(
@@ -137,9 +138,44 @@ public class RegexpGradleScannerTest extends NbTestCase {
         checkDependencyMap(scanner, deps);
     }
     
+    
+    
+    /**
+     * Checks that the container is properly reported. For single 
dependencies, the container is null.
+     * For dependency blocks like compileOnly {...}, the container is the 
'compileOnly" block.
+     * @throws Exception 
+     */
+    public void testDependencyContainers() throws Exception {
+        FileObject f = 
FileUtil.toFileObject(getDataDir()).getFileObject("dependencies/parse/variousSyntax.gradle");
+        TextDependencyScanner scanner = new TextDependencyScanner(true);
+        
+        filteredText = filterAndStorePositions(f.asText());
+        scanner.withConfigurations(Arrays.asList(
+            "runtimeOnly", "implementation", "annotationProcessor"
+        ));
+        List<DependencyText> deps = scanner.parseDependencyList(filteredText);
+        DependencyText text = deps.stream().filter(d -> 
"io.micronaut:micronaut-http-validation".equals(d.contents)).findAny().get();
+        assertNull(text.container); // no specific container
+        
+        text = deps.stream().filter(d -> 
"io.micronaut:micronaut-jackson-databind".equals(d.contents)).findAny().get();
+        assertNotNull("Container is found for string lists", text.container);
+        assertEquals("implementation", text.container.containerPart.partId);
+        assertEquals((int)startPosition.get("P"), 
text.container.containerPart.startPos);
+        assertEquals((int)startPosition.get("Q"), 
text.container.containerPart.endPos);
+        
+        text = deps.stream().filter(d -> 
"ch.qos.logback:logback-classic".equals(d.contents)).findAny().get();
+        assertNull("Parser is not fooled by braced customization", 
text.container); // no specific container
+
+        text = deps.stream().filter(d -> 
"org.ow2.asm".equals(d.group)).findAny().get();
+        assertNotNull("Container is found for map lists", text.container);
+        assertEquals("runtimeOnly", text.container.containerPart.partId);
+        assertEquals((int)startPosition.get("R"), 
text.container.containerPart.startPos);
+        assertEquals((int)startPosition.get("S"), 
text.container.containerPart.endPos);
+    }
+    
     public void testMapLikeDeclaration() throws Exception {
         FileObject f = 
FileUtil.toFileObject(getDataDir()).getFileObject("dependencies/parse/variousSyntax.gradle");
-        TextDependencyScanner scanner = new TextDependencyScanner();
+        TextDependencyScanner scanner = new TextDependencyScanner(true);
         
         filteredText = filterAndStorePositions(f.asText());
         scanner.withConfigurations(Arrays.asList(
@@ -154,17 +190,42 @@ public class RegexpGradleScannerTest extends NbTestCase {
     private Map<String, Integer> startPosition = new HashMap<>();
     private Map<String, Integer> endPosition = new HashMap<>();
     
+    /**
+     * Stub that only serves as an identifier, cannot answer imply/inherit 
questions.
+     */
+    private static final class ScopeStub extends Scope {
+        public ScopeStub(String name) {
+            super(name);
+        }
+
+        public boolean includes(Scope s) {
+            return false;
+        }
+
+        public boolean exports(Scope s) {
+            return false;
+        }
+
+        public boolean implies(Scope s) {
+            return false;
+        }
+    }
+    
+    private Scope s(String name) {
+        return new ScopeStub(name);
+    }
+    
     private void checkDependencyMap(TextDependencyScanner scanner, 
List<DependencyText> deps) {
         List<Dependency> list = new ArrayList<>();
         for (DependencyText t : deps) {
             if (t.keyword == null && t.name != null && t.group != null && 
t.version != null) {
                 ArtifactSpec as = ArtifactSpec.builder(t.group, t.name, 
t.version, null).build();
-                Dependency d = Dependency.create(as, Scopes.RUNTIME, 
Collections.emptyList(), null);
+                Dependency d = Dependency.create(as, s(t.configuration), 
Collections.emptyList(), null);
                 list.add(d);
             } else if ("project".equals(t.keyword)) {
                 ProjectSpec p = ProjectSpec.create(t.contents, null);
                 ArtifactSpec as = ArtifactSpec.builder(t.group, t.name, 
t.version, null).build();
-                Dependency d = Dependency.create(p, as, Scopes.RUNTIME, 
Collections.emptyList(), null);
+                Dependency d = Dependency.create(p, as, s(t.configuration), 
Collections.emptyList(), null);
                 list.add(d);
             }
         }
diff --git 
a/java/maven/src/org/netbeans/modules/maven/queries/MavenDependenciesImplementation.java
 
b/java/maven/src/org/netbeans/modules/maven/queries/MavenDependenciesImplementation.java
index b3a56b0bb2..2c561c12b7 100644
--- 
a/java/maven/src/org/netbeans/modules/maven/queries/MavenDependenciesImplementation.java
+++ 
b/java/maven/src/org/netbeans/modules/maven/queries/MavenDependenciesImplementation.java
@@ -18,31 +18,26 @@
  */
 package org.netbeans.modules.maven.queries;
 
-import java.net.URI;
-import java.nio.file.Files;
-import java.nio.file.InvalidPathException;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayDeque;
+import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.Queue;
 import java.util.Set;
-import java.util.logging.Level;
 import java.util.logging.Logger;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 import org.apache.maven.MavenExecutionException;
 import org.apache.maven.artifact.Artifact;
-import org.apache.maven.model.Model;
 import org.apache.maven.project.MavenProject;
 import org.codehaus.plexus.PlexusContainerException;
 import org.netbeans.api.project.Project;
+import org.netbeans.modules.maven.NbMavenProjectImpl;
 import org.netbeans.modules.maven.api.NbMavenProject;
 import org.netbeans.modules.maven.embedder.DependencyTreeFactory;
 import org.netbeans.modules.maven.embedder.EmbedderFactory;
@@ -93,19 +88,51 @@ public class MavenDependenciesImplementation implements 
ProjectDependenciesImple
         }
     }
     
-    static final Map<Scope, String> mavenScopes;
+    /**
+     * Mapping from the abstract scopes to Maven
+     */
+    static final Map<Scope, String> scope2Maven = new HashMap<>();
+
+    /**
+     * Mapping from maven to the abstract scopes
+     */
+    static final Map<String, Scope> maven2Scope = new HashMap<>();
+    
+    static final Map<Scope, Collection<Scope>> directScopes = new HashMap<>();
+    static final Map<Scope, Collection<Scope>> impliedScopes = new HashMap<>();
+    static final Map<Scope, Collection<Scope>> reverseImplied = new 
HashMap<>();
     
     static {
-        mavenScopes = new HashMap<>();
-        mavenScopes.put(Scopes.PROCESS, "compile");
-        mavenScopes.put(Scopes.COMPILE, "compile");
-        mavenScopes.put(Scopes.RUNTIME, "runtime");
-        mavenScopes.put(Scopes.TEST, "test");
-        mavenScopes.put(Scopes.EXTERNAL, "provided");
+        scope2Maven.put(Scopes.PROCESS, "compile");
+        scope2Maven.put(Scopes.COMPILE, "compile");
+        scope2Maven.put(Scopes.RUNTIME, "runtime");
+        scope2Maven.put(Scopes.TEST, "test");
+        scope2Maven.put(Scopes.EXTERNAL, "provided");
+        
+        maven2Scope.put("compile", Scopes.COMPILE);
+        maven2Scope.put("runtime", Scopes.RUNTIME);
+        maven2Scope.put("test", Scopes.TEST);
+        maven2Scope.put("provided", Scopes.EXTERNAL);
+        
+        directScopes.put(Scopes.API, Arrays.asList(Scopes.COMPILE));
+        directScopes.put(Scopes.PROCESS, Arrays.asList(Scopes.COMPILE));
+        directScopes.put(Scopes.EXTERNAL, Arrays.asList(Scopes.COMPILE));
+        directScopes.put(Scopes.COMPILE, Arrays.asList(Scopes.RUNTIME, 
Scopes.TEST));
+        directScopes.put(Scopes.RUNTIME, Arrays.asList(Scopes.TEST));
+        
+        impliedScopes.put(Scopes.API, Arrays.asList(Scopes.COMPILE, 
Scopes.RUNTIME, Scopes.TEST));
+        impliedScopes.put(Scopes.PROCESS, Arrays.asList(Scopes.COMPILE, 
Scopes.RUNTIME, Scopes.TEST));
+        impliedScopes.put(Scopes.EXTERNAL, Arrays.asList(Scopes.COMPILE, 
Scopes.RUNTIME, Scopes.TEST));
+        impliedScopes.put(Scopes.COMPILE, Arrays.asList(Scopes.RUNTIME, 
Scopes.TEST));
+        impliedScopes.put(Scopes.RUNTIME, Arrays.asList(Scopes.TEST));
+        
+        reverseImplied.put(Scopes.TEST, Arrays.asList(Scopes.RUNTIME, 
Scopes.COMPILE, Scopes.API,Scopes.EXTERNAL, Scopes.PROCESS));
+        reverseImplied.put(Scopes.RUNTIME, Arrays.asList(Scopes.COMPILE, 
Scopes.API, Scopes.EXTERNAL, Scopes.PROCESS));
+        reverseImplied.put(Scopes.COMPILE, Arrays.asList(Scopes.API, 
Scopes.EXTERNAL, Scopes.PROCESS));
     }
     
     static String mavenScope(Scope s) {
-        return mavenScopes.getOrDefault(s, "runtime");
+        return scope2Maven.getOrDefault(s, "compile");
     }
     
     private ArtifactSpec mavenToArtifactSpec(Artifact a) {
@@ -119,6 +146,55 @@ public class MavenDependenciesImplementation implements 
ProjectDependenciesImple
         }
     }
     
+    /**
+     * Returns dependencies declared right in the POM file. Respects the 
user's query filter for artifacts.
+     * @param query
+     * @param embedder
+     * @return 
+     */
+    private DependencyResult 
findDeclaredDependencies(ProjectDependencies.DependencyQuery query, 
MavenEmbedder embedder) {
+        NbMavenProjectImpl impl = 
(NbMavenProjectImpl)project.getLookup().lookup(NbMavenProjectImpl.class);
+        MavenProject proj = impl.getFreshOriginalMavenProject();
+        List<Dependency> children = new ArrayList<>();
+        for (org.apache.maven.model.Dependency d : proj.getDependencies()) {
+            String aId = d.getArtifactId();
+            String gID = d.getGroupId();
+            String scope = d.getScope();
+            String classsifier = d.getClassifier();
+            String type = d.getType();
+            String version = d.getVersion();
+            
+            ArtifactSpec a;
+            
+            if (version != null && version.endsWith("-SNAPSHOT")) {
+                a = ArtifactSpec.createSnapshotSpec(gID, aId, type, 
classsifier, version, d.isOptional(), 
+                        d.getSystemPath() == null ? null : 
FileUtil.toFileObject(new File(d.getSystemPath(), aId)), d);
+            } else {            
+                a = ArtifactSpec.createVersionSpec(gID, aId, type, 
classsifier, version, d.isOptional(), 
+                        d.getSystemPath() == null ? null : 
FileUtil.toFileObject(new File(d.getSystemPath(), aId)), d);
+            }
+            Scope s = scope == null ? Scopes.COMPILE : maven2Scope.get(scope);
+            if (s == null) {
+                s = Scopes.COMPILE;
+            }
+            Dependency dep = Dependency.create(a, s, Collections.emptyList(), 
d);
+            children.add(dep);
+        }
+
+        ArtifactSpec prjSpec = mavenToArtifactSpec(proj.getArtifact());
+        Dependency root = Dependency.create(prjSpec, Scopes.DECLARED, 
children, proj);
+        
+        return new MavenDependencyResult(proj, root, query.getScopes(), 
Collections.emptyList(), project, impl.getProjectWatcher());
+    }
+    
+    static Collection<Scope> implies(Scope s) {
+        return impliedScopes.getOrDefault(s, Collections.emptyList());
+    }
+    
+    static Collection<Scope> impliedBy(Scope s) {
+        return reverseImplied.getOrDefault(s, Collections.emptyList());
+    }
+    
     @NbBundle.Messages({
         "ERR_DependencyOnBrokenProject=Unable to collect dependencies from a 
broken project",
         "ERR_DependencyNotPrimed=Unable to collect dependencies from a broken 
project",
@@ -162,8 +238,13 @@ public class MavenDependenciesImplementation implements 
ProjectDependenciesImple
             }
         }
         
+        if (query.getScopes().contains(Scopes.DECLARED)) {
+            return findDeclaredDependencies(query, embedder);
+        }
+        
         Collection<String> mavenScopes = scopes.stream().
                 map(MavenDependenciesImplementation::mavenScope).
+                filter(Objects::nonNull).
                 collect(Collectors.toList());
         
         org.apache.maven.shared.dependency.tree.DependencyNode n;
@@ -179,24 +260,7 @@ public class MavenDependenciesImplementation implements 
ProjectDependenciesImple
                 throw e;
             }
         }
-        Set<Scope> allScopes = new HashSet<>();
-        
-        Queue<Scope> processScopes = new ArrayDeque<>(scopes);
-        while (!processScopes.isEmpty()) {
-            Scope s = processScopes.poll();
-            Set<Scope> newScopes = new HashSet<>();
-            newScopes.add(s);
-            for (Scope t : SCOPES) {
-                if (s.includes(t)) {
-                    newScopes.add(t);
-                } else if (t.implies(s)) {
-                    newScopes.add(t);
-                }
-            }
-            newScopes.removeAll(allScopes);
-            allScopes.addAll(newScopes);
-            processScopes.addAll(newScopes);
-        }
+        Set<Scope> allScopes = Stream.concat(scopes.stream(), 
scopes.stream().flatMap(x -> 
impliedBy(x).stream())).collect(Collectors.toSet());
         Set<ArtifactSpec> broken = new HashSet<>();
         Dependency.Filter compositeFiter = new Dependency.Filter() {
             @Override
diff --git 
a/java/maven/src/org/netbeans/modules/maven/queries/MavenDependencyResult.java 
b/java/maven/src/org/netbeans/modules/maven/queries/MavenDependencyResult.java
index 685ddd8800..c9509cffda 100644
--- 
a/java/maven/src/org/netbeans/modules/maven/queries/MavenDependencyResult.java
+++ 
b/java/maven/src/org/netbeans/modules/maven/queries/MavenDependencyResult.java
@@ -28,6 +28,7 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import javax.swing.event.ChangeEvent;
 import javax.swing.event.ChangeListener;
 import javax.swing.event.DocumentEvent;
@@ -55,6 +56,7 @@ import org.netbeans.modules.maven.model.pom.POMModelFactory;
 import org.netbeans.modules.project.dependency.ArtifactSpec;
 import org.netbeans.modules.project.dependency.Dependency;
 import org.netbeans.modules.project.dependency.DependencyResult;
+import org.netbeans.modules.project.dependency.ProjectScopes;
 import org.netbeans.modules.project.dependency.Scope;
 import org.netbeans.modules.project.dependency.SourceLocation;
 import org.netbeans.modules.xml.xam.ModelSource;
@@ -70,7 +72,7 @@ import org.openide.util.WeakListeners;
  *
  * @author sdedic
  */
-class MavenDependencyResult implements 
org.netbeans.modules.project.dependency.DependencyResult, 
PropertyChangeListener {
+class MavenDependencyResult implements 
org.netbeans.modules.project.dependency.DependencyResult, ProjectScopes, 
PropertyChangeListener {
     final Project ideProject;
     final NbMavenProject mavenProject;
     final Dependency rootNode;
@@ -94,6 +96,21 @@ class MavenDependencyResult implements 
org.netbeans.modules.project.dependency.D
         this.problems = problems;
     }
 
+    @Override
+    public ProjectScopes getScopes() {
+        return this;
+    }
+
+    @Override
+    public Collection<? extends Scope> scopes() {
+        return MavenDependenciesImplementation.scope2Maven.keySet();
+    }
+
+    @Override
+    public Collection<? extends Scope> implies(Scope s, boolean direct) {
+        return (direct ? MavenDependenciesImplementation.directScopes : 
MavenDependenciesImplementation.impliedScopes).getOrDefault(s, 
Collections.emptySet());
+    }
+
     @Override
     public Collection<FileObject> getDependencyFiles() {
         File file = mavenProject.getMavenProject().getFile();


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists

Reply via email to