blob: a0376569dfaf74f2605c887c433c7051bad71c74 [file] [log] [blame]
/*
* 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.
*/
/*
* Contributor(s): theanuradha@netbeans.org
*/
package org.netbeans.modules.maven.api;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.namespace.QName;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.apache.maven.model.InputLocation;
import org.apache.maven.model.InputSource;
import org.apache.maven.model.building.ModelProblem;
import org.apache.maven.project.MavenProject;
import org.apache.maven.repository.RepositorySystem;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.annotations.common.SuppressWarnings;
import org.netbeans.api.project.libraries.Library;
import org.netbeans.modules.maven.model.ModelOperation;
import org.netbeans.modules.maven.model.Utilities;
import static org.netbeans.modules.maven.model.Utilities.openAtPosition;
import org.netbeans.modules.maven.model.pom.Build;
import org.netbeans.modules.maven.model.pom.Configuration;
import org.netbeans.modules.maven.model.pom.Dependency;
import org.netbeans.modules.maven.model.pom.DependencyManagement;
import org.netbeans.modules.maven.model.pom.POMComponent;
import org.netbeans.modules.maven.model.pom.POMExtensibilityElement;
import org.netbeans.modules.maven.model.pom.POMModel;
import org.netbeans.modules.maven.model.pom.Plugin;
import org.netbeans.modules.maven.model.pom.PluginManagement;
import org.netbeans.modules.maven.model.pom.Project;
import org.netbeans.modules.maven.model.pom.Repository;
import org.netbeans.modules.maven.options.MavenSettings;
import org.netbeans.modules.maven.options.MavenVersionSettings;
import org.netbeans.modules.maven.spi.nodes.NodeUtils;
import org.openide.cookies.EditCookie;
import org.openide.cookies.LineCookie;
import org.openide.filesystems.FileObject;
import org.openide.loaders.DataObject;
import org.openide.loaders.DataObjectNotFoundException;
import org.openide.text.Line;
/**
* Various maven model related utilities.
* @author mkleint
* @author Anuradha G
*/
public final class ModelUtils {
private static final Logger LOG = Logger.getLogger(ModelUtils.class.getName());
/**
* library descriptor property containing whitespace separated list of maven coordinate values groupid:artifactId:version:[classifier:]type
*/
public static final String LIBRARY_PROP_DEPENDENCIES = "maven-dependencies";
/**
* library descriptor property containing whitespace separated list of maven coordinate values repo_type:repo_url
*/
public static final String LIBRARY_PROP_REPOSITORIES = "maven-repositories";
private ModelUtils() {}
/**
*
* @param pom FileObject that represents POM
* @param group
* @param artifact
* @param version
* @param type
* @param scope
* @param classifier
* @param acceptNull accept null values to scope,type and classifier.
* If true null values will remove corresponding tag.
*/
public static void addDependency(FileObject pom,
final String group,
final String artifact,
final String version,
final String type,
final String scope,
final String classifier, final boolean acceptNull)
{
ModelOperation<POMModel> operation = new ModelOperation<POMModel>() {
private static final String BUNDLE_TYPE = "bundle"; //NOI18N
@Override
public void performOperation(POMModel model) {
Dependency dep = checkModelDependency(model, group, artifact, true);
dep.setVersion(version);
if (acceptNull || scope != null) {
dep.setScope(scope);
}
if (acceptNull || (type != null && !BUNDLE_TYPE.equals(type))) {
dep.setType(type);
}
if (acceptNull || classifier != null) {
dep.setClassifier(classifier);
}
}
};
Utilities.performPOMModelOperations(pom, Collections.singletonList(operation));
}
public static Dependency checkModelDependency(POMModel pom, String groupId, String artifactId, boolean add) {
Project mdl = pom.getProject();
Dependency ret = mdl.findDependencyById(groupId, artifactId, null);
Dependency managed = null;
if (ret == null || ret.getVersion() == null) {
//check dependency management section as well..
DependencyManagement mng = mdl.getDependencyManagement();
if (mng != null) {
managed = mng.findDependencyById(groupId, artifactId, null);
}
}
if (add && ret == null) {
ret = mdl.getModel().getFactory().createDependency();
ret.setGroupId(groupId);
ret.setArtifactId(artifactId);
mdl.addDependency(ret);
}
// if managed dependency section is present, return that one for editing..
return managed == null ? ret : managed;
}
public static boolean hasModelDependency(POMModel mdl, String groupid, String artifactid) {
return checkModelDependency(mdl, groupid, artifactid, false) != null;
}
/**
* Opens a pom file at location defined in InputLocation parameter
* @since 2.77
* @param location
*/
public static void openAtSource(InputLocation location) {
InputSource source = location.getSource();
if (source != null && source.getLocation() != null) {
FileObject fobj = FileUtilities.convertStringToFileObject(source.getLocation());
if (fobj != null) {
try {
DataObject dobj = DataObject.find(NodeUtils.readOnlyLocalRepositoryFile(fobj));
EditCookie edit = dobj.getLookup().lookup(EditCookie.class);
if (edit != null) {
edit.edit();
}
LineCookie lc = dobj.getLookup().lookup(LineCookie.class);
lc.getLineSet().getOriginal(location.getLineNumber() - 1).show(Line.ShowOpenType.REUSE, Line.ShowVisibilityType.FOCUS, location.getColumnNumber() - 1);
} catch (DataObjectNotFoundException ex) {
LOG.log(Level.FINE, "dataobject not found", ex);
}
}
}
}
/**
* Opens pom at a plugin with the given groupId and artifactId.
*
* @param model the model to open
* @param groupId the plugin groupId
* @param artifactId the plugin artifactId
*/
public static void openAtPlugin(POMModel model, String groupId, String artifactId) {
int pos = -1;
org.netbeans.modules.maven.model.pom.Project p = model.getProject();
Build bld = p.getBuild();
if (bld != null) {
Plugin plg = bld.findPluginById(groupId, artifactId);
if (plg != null) {
pos = plg.findPosition();
}
}
if(pos == -1) {
pos = p.findPosition();
}
if(pos == -1) {
return;
}
openAtPosition(model, pos);
}
public static void updatePluginVersion(String groupId, String artifactId, String version, org.netbeans.modules.maven.model.pom.Project prj) {
Build bld = prj.getBuild();
boolean setInPM = false;
boolean setInPlgs = false;
if (bld != null) {
PluginManagement pm = bld.getPluginManagement();
if (pm != null) {
Plugin p = pm.findPluginById(groupId, artifactId);
if (p != null) {
p.setVersion(version);
setInPM = true;
}
}
Plugin p = bld.findPluginById(groupId, artifactId);
if (p != null) {
if (p.getVersion() != null) {
p.setVersion(version);
setInPlgs = true;
} else {
if (!setInPM) {
p.setVersion(version);
setInPlgs = true;
}
}
}
}
if (!setInPM && !setInPlgs) {
if (bld == null) {
bld = prj.getModel().getFactory().createBuild();
prj.setBuild(bld);
}
Plugin p = prj.getModel().getFactory().createPlugin();
p.setGroupId(groupId);
p.setArtifactId(artifactId);
p.setVersion(version);
bld.addPlugin(p);
}
}
/**
*
* @param mdl
* @param url of the repository
* @param add true == add to model, will not add if the repo is in project but not in model (eg. central repo)
* @return null if repository with given url exists, otherwise a returned newly created item.
*/
public static Repository addModelRepository(MavenProject project, POMModel mdl, String url) {
if (url.contains(RepositorySystem.DEFAULT_REMOTE_REPO_URL) || /* #212336 */url.contains("http://repo1.maven.org/maven2")) {
return null;
}
List<Repository> repos = mdl.getProject().getRepositories();
if (repos != null) {
for (Repository r : repos) {
if (url.equals(r.getUrl())) {
//already in model..either in pom.xml or added in this session.
return null;
}
}
}
List<org.apache.maven.model.Repository> reps = project.getRepositories();
org.apache.maven.model.Repository prjret = null;
Repository ret = null;
if (reps != null) {
for (org.apache.maven.model.Repository re : reps) {
if (url.equals(re.getUrl())) {
prjret = re;
break;
}
}
}
if (prjret == null) {
ret = mdl.getFactory().createRepository();
ret.setUrl(url);
ret.setId(url);
mdl.getProject().addRepository(ret);
}
return ret;
}
public static boolean checkByCLIMavenValidationLevel(ModelProblem problem) {
// XXX HACK - this should be properly solved by upgrading the embeded maven
String version = MavenSettings.getCommandLineMavenVersion();
try {
if ( version != null && !"".equals(version.trim()) &&
new DefaultArtifactVersion(version).compareTo(new DefaultArtifactVersion("3.2.1")) > 0)
{
if ( (problem.getMessage().startsWith("'dependencies.dependency.exclusions.exclusion.groupId' for ") ||
problem.getMessage().startsWith("'dependencies.dependency.exclusions.exclusion.artifactId' for "))
&& problem.getMessage().contains(" with value '*' does not match a valid id pattern"))
{
return false;
}
}
} catch (Throwable e) {
// ignore and be optimistic about the hint
LOG.log(Level.INFO, version, e);
}
return true;
}
/**
* Sets the Java source level of a project.
* Use {@link PluginPropertyUtils#getPluginProperty(Project,String,String,String,String)} first
* ({@link Constants#GROUP_APACHE_PLUGINS}, {@link Constants#PLUGIN_COMPILER}, {@link Constants#SOURCE_PARAM}, {@code "compile"})
* to make sure that the current level is actually not what you want.
*
* Please Note: THis method will not take existing configuration into account, especially the use of property (maven.compiler.source, maven.compiler.target)
* instead of plugin configuration is ignored.
* @param mdl a POM model
* @param sourceLevel the desired source level
* @since 2.19
*/
public static void setSourceLevel(POMModel mdl, String sourceLevel) {
Plugin old = null;
Plugin plugin;
Build bld = mdl.getProject().getBuild();
if (bld != null) {
old = bld.findPluginById(Constants.GROUP_APACHE_PLUGINS, Constants.PLUGIN_COMPILER);
} else {
mdl.getProject().setBuild(mdl.getFactory().createBuild());
}
if (old != null) {
plugin = old;
} else {
plugin = mdl.getFactory().createPlugin();
plugin.setGroupId(Constants.GROUP_APACHE_PLUGINS);
plugin.setArtifactId(Constants.PLUGIN_COMPILER);
plugin.setVersion(MavenVersionSettings.getDefault().getVersion(MavenVersionSettings.VERSION_COMPILER));
mdl.getProject().getBuild().addPlugin(plugin);
}
Configuration conf = plugin.getConfiguration();
if (conf == null) {
conf = mdl.getFactory().createConfiguration();
plugin.setConfiguration(conf);
}
conf.setSimpleParameter(Constants.SOURCE_PARAM, sourceLevel);
conf.setSimpleParameter(Constants.TARGET_PARAM, sourceLevel);
}
/**
* Returns child element of given parent, specified by its local name.
* Creates such child in case it doesn't exist.
*
* @param parent parent element
* @param localQName local name of the child
* @param pomModel whole pom model
* @return existing or newly created child
*/
public static POMExtensibilityElement getOrCreateChild (POMComponent parent, String localQName, POMModel pomModel) {
POMExtensibilityElement result = null;
for (POMExtensibilityElement el : parent.getExtensibilityElements()) {
if (localQName.equals(el.getQName().getLocalPart())) {
result = el;
break;
}
}
if (result == null) {
result = pomModel.getFactory().
createPOMExtensibilityElement(new QName(localQName));
parent.addExtensibilityElement(result);
}
return result;
}
private static final String PROBABLE_ROOTS
= "maven2|" // mainly for Central
+ "maven[.]repo|" // often used for Eclipse repos
+ "content/(?:groups|repositories|shadows)/[^/]+|" // Nexus
+ ".+(?=/(?:javax|org|net|com)/)"; // common groupId starters
/**
* 1 - root
* 2 - groupId as slashes
* 3 - artifactId
* 4 - version
*/
private static Pattern DEFAULT = Pattern.compile("(.+://[^/]+/(?:(?:.+/)?(?:" + PROBABLE_ROOTS + ")/)?)(.+)/([^/]+)/([^/]+)/\\3-\\4[.]pom");
/**
* 1 - root
* 2 - groupId
* 3 - artifactId
* 4 - version
*/
private static Pattern LEGACY = Pattern.compile("(.+/)([^/]+)/poms/([a-zA-Z0-9_]+[a-zA-Z_-]+)-([0-9].+)[.]pom");
/** Returns a library descriptor corresponding to the given library,
* or null if not recognized successfully.
*
* @param pom library to check
* @return LibraryDescriptor corresponding to the library, or null if the pom URL format is not recognized.
* @deprecated in favor of <code>checkLibraries(Library)</code>
*/
@SuppressWarnings("SBSC_USE_STRINGBUFFER_CONCATENATION")
@Deprecated
public static LibraryDescriptor checkLibrary(URL pom) {
String pomS;
try {
pomS = new URL(pom.getProtocol(), pom.getHost(), pom.getPort(), pom.getFile()).toString(); // strip ref
} catch (MalformedURLException x) {
pomS = pom.toString();
}
Matcher m1 = LEGACY.matcher(pomS);
if (m1.matches()) {
return new LibraryDescriptor("legacy", m1.group(1), m1.group(2), m1.group(3), m1.group(4), pom.getRef(), "jar");
}
Matcher m2 = DEFAULT.matcher(pomS);
if (m2.matches()) {
return new LibraryDescriptor("default", m2.group(1), m2.group(2).replace('/', '.'), m2.group(3), m2.group(4), pom.getRef(), "jar");
}
return null;
}
/**
* return a descriptor for entire <code>Library</code> containing recognized dependencies and repositories,
* both in old (volume) and new (properties) format
* @param library
* @return
*/
public static Descriptor checkLibraries(Library library) {
return checkLibraries(library.getProperties());
}
//for tests
static Descriptor checkLibraries(Map<String, String> properties) {
List<LibraryDescriptor> libs = new ArrayList<LibraryDescriptor>();
List<RepositoryDescriptor> reps = new ArrayList<RepositoryDescriptor>();
String dependencies = properties.get(LIBRARY_PROP_DEPENDENCIES);
if (dependencies != null) {
//http://www.netbeans.org/ns/library-declaration/3
for (String dep : dependencies.split("([\\s])+")) {
String[] v = dep.trim().split(":");
if (v.length < 4) {
//TODO log.
continue;
}
String grid = v[0];
String art = v[1];
String ver = v[2];
String type = v.length == 4 ? v[3] : v[4];
String classifier = v.length >= 5 ? v[3] : null;
libs.add(new LibraryDescriptor(null, null, grid, art, ver, classifier, type));
}
String repositories = properties.get(LIBRARY_PROP_REPOSITORIES);
if (repositories != null) {
for (String r : repositories.split("([\\s])+")) {
String[] rep = r.split(":", 2);
if (rep.length < 2) {
//TODO log.
continue;
}
reps.add(new RepositoryDescriptor(rep[0], rep[1]));
}
}
}
return new Descriptor(libs, reps);
}
public static final class Descriptor {
final List<LibraryDescriptor> dependencies;
final List<RepositoryDescriptor> repositories;
Descriptor(@NonNull List<LibraryDescriptor> dependencies, @NonNull List<RepositoryDescriptor> repositories) {
this.dependencies = dependencies;
this.repositories = repositories;
}
public List<LibraryDescriptor> getDependencies() {
return dependencies;
}
public List<RepositoryDescriptor> getRepositories() {
return repositories;
}
}
public static final class RepositoryDescriptor {
private final String repoType /* default/legacy */, repoRoot;
RepositoryDescriptor(String repoType, String repoRoot) {
this.repoType = repoType;
this.repoRoot = repoRoot;
}
public String getRepoType() {
return repoType;
}
public String getRepoRoot() {
return repoRoot;
}
@Override
public int hashCode() {
int hash = 5;
hash = 37 * hash + (this.repoType != null ? this.repoType.hashCode() : 0);
hash = 37 * hash + (this.repoRoot != null ? this.repoRoot.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final RepositoryDescriptor other = (RepositoryDescriptor) obj;
if ((this.repoType == null) ? (other.repoType != null) : !this.repoType.equals(other.repoType)) {
return false;
}
if ((this.repoRoot == null) ? (other.repoRoot != null) : !this.repoRoot.equals(other.repoRoot)) {
return false;
}
return true;
}
}
public static final class LibraryDescriptor {
private final String repoType /* default/legacy */, repoRoot, groupId, artifactId,
version, classifier /* optional, not part of path, but url's ref */, type;
LibraryDescriptor(String repoType, String repoRoot, String groupId, String artifactId, String version, String classifier, String type) {
this.repoType = repoType;
this.repoRoot = repoRoot;
this.groupId = groupId;
this.artifactId = artifactId;
this.version = version;
this.classifier = classifier;
this.type = type;
}
public String getArtifactId() {
return artifactId;
}
/** May return null. */
public String getClassifier() {
return classifier;
}
public String getGroupId() {
return groupId;
}
@Deprecated
public String getRepoRoot() {
return repoRoot;
}
@Deprecated
public String getRepoType() {
return repoType;
}
public String getVersion() {
return version;
}
public String getType() {
return type;
}
@Override public String toString() {
return "LibraryDescriptor{" + "repoType=" + repoType + ", repoRoot=" + repoRoot + ", groupId=" + groupId + ", artifactId=" + artifactId + ", version=" + version + ", classifier=" + classifier + ", type=" + type + '}';
}
}
}