Skip to content

Commit

Permalink
Initial implementation of Project Dependency API
Browse files Browse the repository at this point in the history
  • Loading branch information
sdedic committed May 25, 2022
1 parent 5d0fb43 commit b96970e
Show file tree
Hide file tree
Showing 72 changed files with 2,752 additions and 84 deletions.
14 changes: 14 additions & 0 deletions ide/project.dependency/Readme.md
@@ -0,0 +1,14 @@
# Project Dependencies

This module is highly experimental; provides an abstract API to query project dependncies
that shall be implemented by
- gradle
- maven
- ant/nbm support
At this moment, just Maven implements it and I need to validate the API design by adding
the other implementations before the API is going to be published officially. In the
meantime, implementation dependencies are required to access / implement the API.

The code will **eventually merge** into `project.api` module and this experimental one
will be deleted.

@@ -0,0 +1,193 @@
/*
* 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.Objects;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.annotations.common.NullAllowed;

/**
* Represents an artifact. Each artifact is identified by
* <ul>
* <li>group or organization id
* <li>artifact id
* <li>version
* <li>(optional) classifier; no classifier shall be interpreted as a
* regular build artifact
* <li>(optional) type; not type shall be interepreted as the default type
* for the processing compiler or builder
* </ul>
* The version specified is further classified by {@link VersionKind}, to
* distinguish versions possibly from repositories, development versions and
* floating versions.
*
* @author sdedic
*/
public final class ArtifactSpec<T> {
/**
* Kind of the artifact version
*/
public enum VersionKind {
/**
* Regular publishable artifact
*/
REGULAR,

/**
* Snapshot artifact
*/
SNAPSHOT,

/**
* Regular artifact, floating version
*/
LATEST
};

private final VersionKind kind;
private final String type;
private final String groupId;
private final String artifactId;
private final String versionSpec;
private final String classifier;
private final boolean optional;
final T data;

ArtifactSpec(VersionKind kind, String groupId, String artifactId, String versionSpec, String type, String classifier, boolean optional, T impl) {
this.kind = kind;
this.groupId = groupId;
this.artifactId = artifactId;
this.versionSpec = versionSpec;
this.classifier = classifier;
this.optional = optional;
this.data = impl;
this.type = type;
}

public VersionKind getKind() {
return kind;
}

public String getType() {
return type;
}

public String getGroupId() {
return groupId;
}

public String getArtifactId() {
return artifactId;
}

public String getVersionSpec() {
return versionSpec;
}

public String getClassifier() {
return classifier;
}

public boolean isOptional() {
return optional;
}

@Override
public int hashCode() {
int hash = 7;
hash = 79 * hash + Objects.hashCode(this.kind);
hash = 79 * hash + Objects.hashCode(this.type);
hash = 79 * hash + Objects.hashCode(this.groupId);
hash = 79 * hash + Objects.hashCode(this.artifactId);
hash = 79 * hash + Objects.hashCode(this.versionSpec);
hash = 79 * hash + Objects.hashCode(this.classifier);
return hash;
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ArtifactSpec<?> other = (ArtifactSpec<?>) obj;
if (!Objects.equals(this.type, other.type)) {
return false;
}
if (!Objects.equals(this.groupId, other.groupId)) {
return false;
}
if (!Objects.equals(this.artifactId, other.artifactId)) {
return false;
}
if (!Objects.equals(this.versionSpec, other.versionSpec)) {
return false;
}
if (!Objects.equals(this.classifier, other.classifier)) {
return false;
}
return this.kind == other.kind;
}

public String toString() {
StringBuilder sb = new StringBuilder(
String.format("%s:%s:%s", getGroupId(), getArtifactId(), getVersionSpec() == null ? "" : getVersionSpec())
);
if (classifier != null) {
sb.append(":").append(classifier);
}
if (type != null) {
sb.append("[").append(type).append("]");
}
if (optional) {
sb.append("?");
}
return sb.toString();
}

/**
* Returns opaque project-specific data. If searching for
* a project-specific extension, use {@link ProjectDependencies#findAdapters} instead.
*
* @return unspecified underlying project data
*/
public T getProjectData() {
return data;
}

public static <V> ArtifactSpec<V> createVersionSpec(
@NonNull String groupId, @NonNull String artifactId,
@NullAllowed String type, @NullAllowed String classifier,
@NonNull String versionSpec, boolean optional, @NonNull V data) {
return new ArtifactSpec<V>(VersionKind.REGULAR, groupId, artifactId, versionSpec, type, classifier, optional, data);
}

public static <V> ArtifactSpec<V> createSnapshotSpec(
@NonNull String groupId, @NonNull String artifactId,
@NullAllowed String type, @NullAllowed String classifier,
@NonNull String versionSpec, boolean optional, @NonNull V data) {
return new ArtifactSpec<V>(VersionKind.SNAPSHOT, groupId, artifactId, versionSpec, type, classifier, false, data);
}

}
@@ -0,0 +1 @@
OpenIDE-Module-Name=Project Dependencies
@@ -0,0 +1,84 @@
/*
* 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.List;

/**
* Represents a dependency of an artifact. The {@link #getChildren()} artifacts are
* needed in a certain {@link #getScope()}; should be ignored in unrelated scopes.
* The artifact that is subject of this dependency is the {@link #getArtifact()}.
* Children are expressed using other {@link Dependency} instances.
* <p>
* Dependency does not have well-defined {@link #equals} and {@link #hashCode}, use
* {@link #getArtifact()} as key in Maps.
*
* @author sdedic
*/
public final class Dependency {
private final ArtifactSpec artifact;
private final List<Dependency> children;
private final Scope scope;
final Object data;

Dependency(ArtifactSpec artifact, List<Dependency> children, Scope scope, Object data) {
this.artifact = artifact;
this.children = children;
this.scope = scope;
this.data = data;
}

public ArtifactSpec getArtifact() {
return artifact;
}

public List<Dependency> getChildren() {
return children;
}

public Scope getScope() {
return scope;
}

public Object getProjectData() {
return data;
}

public String toString() {
return getArtifact() + "[" + scope + "]";
}

public static Dependency create(ArtifactSpec artifact, Scope scope, List<Dependency> children, Object data) {
return new Dependency(artifact, children, scope, data);
}

/**
* Allows to filter artifacts and their dependency subtrees.
*/
public interface Filter {
/**
* Decide if the artifact 'a' and its dependencies should be included in the report.
* @param s the scope which requires dependency on "a"
* @param a the artifact
* @return true, if "a" should be included in the result; false to exclude it and its
* dependencies.
*/
public boolean accept(Scope s, ArtifactSpec a);
}
}
@@ -0,0 +1,68 @@
/*
* 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.io.IOException;
import javax.swing.event.ChangeListener;
import org.netbeans.api.project.Project;
import org.openide.util.Lookup;

/**
* Results of a dependency inspection. Contains dependency graph from the {@link #getRoot}.
* The result may become invalid, as a result of project changes or dependency changes.
* The state change will be reported by {@link ChangeListener}. If the client is interested
* in an updated result, it must perform another dependency scan. Once invalid instance
* will never turn back to valid.
* <p>
* The {@link #getLookup() lookup} can be used to search for project-specific services that
* can provide further info on the artifacts or dependencies.
* @author sdedic
*/
public interface DependencyResult extends Lookup.Provider {
/**
* @return the inspected project
*/
public Project getProject();
/**
* The root of the dependency tree. Its artifact should represent the project itself.
* @return project dependency root.
*/
public Dependency getRoot();

public boolean isValid();

/**
* Registers a Listener to be notified when validity changes.
* @param l listener
*/
public void addChangeListener(ChangeListener l);

/**
* Unregisters a previously registered Listener.
* @param l listener
*/
public void removeChangeListener(ChangeListener l);

/**
* Attempts to find location where this dependency is declared.
* @param d
* @return
*/
public SourceLocation getDeclarationRange(Dependency d) throws IOException;
}

0 comments on commit b96970e

Please sign in to comment.