package com.revolsys.maven;
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.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.revolsys.collection.map.LinkedHashMapEx;
import com.revolsys.collection.map.MapEx;
import com.revolsys.collection.map.Maps;
import com.revolsys.logging.Logs;
import com.revolsys.util.Property;
public class MavenPom extends GroupArtifactVersion {
public static String getGroupAndArtifactId(final String id) {
final String[] parts = id.split(":");
if (parts.length < 2) {
return id;
} else {
return parts[0] + ":" + parts[1];
}
}
@SuppressWarnings("unchecked")
public static <T> List<T> getList(final MapEx map, final String key) {
final Object value = map.get(key);
if (value instanceof List) {
return (List<T>)value;
} else if (value == null) {
return Collections.emptyList();
} else {
return (List<T>)Arrays.asList(value);
}
}
private final MavenRepository mavenRepository;
private final MapEx pomProperties;
private MapEx mergedPomProperties;
private String packaging = "jar";
private MavenPom parentPom;
private final List<Dependency> dependencies = new ArrayList<>();
private String mavenId;
private String mavenDependencyId;
public MavenPom(final MavenRepository mavenRepository, final MapEx pom) {
this.mavenRepository = mavenRepository;
final MapEx dependencyContainer = (MapEx)pom.remove("dependencies");
final MapEx pomProperties = Maps.newLinkedHashEx((MapEx)pom.remove("properties"));
setProperties(pom);
final MapEx parent = getProperty("parent");
if (parent != null) {
final String parentGroupId = parent.getString("groupId");
if (getGroupId() == null) {
setGroupId(parentGroupId);
}
final String parentVersion = parent.getString("version");
if (getVersion() == null) {
setVersion(parentVersion);
}
pomProperties.put("project.parent.groupId", parentGroupId);
pomProperties.put("project.parent.version", parentVersion);
}
updateGroupArtifactVersion();
updateMavenId();
this.pomProperties = pomProperties;
if (dependencyContainer != null) {
final List<MapEx> dependencies = getList(dependencyContainer, "dependency");
if (dependencies != null) {
for (final MapEx dependencyMap : dependencies) {
final Dependency dependency = new Dependency(this, dependencyMap);
this.dependencies.add(dependency);
}
}
}
}
@SuppressWarnings({
"rawtypes", "unchecked"
})
public boolean addDependenciesFromTree(final Map<String, String> dependencies,
final String dependencyPath, final Map<String, Map<String, Map>> dependencyTree,
final int depth, final int searchDepth) {
boolean hasChildren = false;
final Set<Entry<String, Map<String, Map>>> entries = dependencyTree.entrySet();
for (final Entry<String, Map<String, Map>> dependencyEntry : entries) {
final String childDependencyId = dependencyEntry.getKey();
final String childPath = dependencyPath + "/" + childDependencyId;
final Map childTree = dependencyEntry.getValue();
final String existingDependencyPath = dependencies.get(childDependencyId);
if (childPath.equals(existingDependencyPath)) {
if (depth < searchDepth) {
if (addDependenciesFromTree(dependencies, childPath, childTree, depth + 1, searchDepth)) {
hasChildren = true;
}
}
} else if (!isDependencyIgnored(dependencies.keySet(), childDependencyId)) {
if (depth == searchDepth) {
dependencies.put(childDependencyId, childPath);
if (!childTree.isEmpty()) {
hasChildren = true;
}
} else if (addDependenciesFromTree(dependencies, childPath, childTree, depth + 1,
searchDepth)) {
hasChildren = true;
}
}
}
return hasChildren;
}
public List<Dependency> getDependencies() {
return this.dependencies;
}
@SuppressWarnings("rawtypes")
public Set<String> getDependencyIds(final Collection<String> exclusionIds) {
final Map<String, String> versions = getDependencyVersions();
final Map<String, Map<String, Map>> dependencyTree = getDependencyTree(versions, exclusionIds,
true);
return getDependencyIdsFromTree(dependencyTree);
}
@SuppressWarnings("rawtypes")
private Set<String> getDependencyIdsFromTree(final Map<String, Map<String, Map>> dependencyTree) {
final Map<String, String> dependencyPaths = new LinkedHashMap<>();
int searchDepth = 0;
while (addDependenciesFromTree(dependencyPaths, "", dependencyTree, 0, searchDepth)) {
searchDepth++;
}
return dependencyPaths.keySet();
}
@SuppressWarnings({
"rawtypes", "unchecked"
})
protected Map<String, Map<String, Map>> getDependencyTree(final Map<String, String> versions,
final Collection<String> exclusionIds, final boolean includeOptional) {
final Map<String, Map<String, Map>> dependencies = new LinkedHashMap<>();
for (final Dependency dependency : this.dependencies) {
final String groupId = dependency.getGroupId();
final String artifactId = dependency.getArtifactId();
final String dependencyKey = groupId + ":" + artifactId;
String version = versions.get(dependencyKey);
if (!Property.hasValue(version)) {
version = dependency.getVersion();
}
final String scope = dependency.getScope();
final boolean optional = dependency.isOptional();
if (scope.equals("compile") && (includeOptional || !optional)) {
if (!Property.hasValue(version)) {
if (groupId.equals(getGroupId())) {
version = getVersion();
}
}
if (!exclusionIds.contains(dependencyKey) && !exclusionIds.contains(groupId + ":*")) {
try {
final MavenPom pom = this.mavenRepository.getPom(groupId, artifactId, version);
if (pom == null) {
Logs.error(this,
"Maven pom not found for " + dependencyKey + ":" + version + " in pom " + this);
} else {
final String dependencyId = pom.getMavenId();
final Set<String> mergedExclusionIds = new HashSet<>(exclusionIds);
mergedExclusionIds.addAll(dependency.getExclusionIds());
// Add child dependencies first so they don't override parent
final Map<String, String> mergedVersions = new HashMap<>();
mergedVersions.putAll(pom.getDependencyVersions());
mergedVersions.putAll(versions);
final Map childDependencyTree = pom.getDependencyTree(mergedVersions,
mergedExclusionIds, false);
dependencies.put(dependencyId, childDependencyTree);
}
} catch (final Exception e) {
throw new IllegalArgumentException("Unable to download pom for " + dependencyKey + ":"
+ version + " in pom " + getMavenId(), e);
}
}
}
}
return dependencies;
}
public Map<String, String> getDependencyVersions() {
final Map<String, String> versions = new HashMap<>();
final MavenPom parent = getParentPom();
if (parent != null) {
versions.putAll(parent.getDependencyVersions());
}
final MapEx dependencyManagement = getProperty("dependencyManagement");
if (dependencyManagement != null) {
final MapEx dependencyMap = dependencyManagement.getValue("dependencies");
if (dependencyMap != null) {
final List<MapEx> dependencyList = getList(dependencyMap, "dependency");
if (dependencyList != null) {
for (final MapEx dependency : dependencyList) {
final String groupId = getMapValue(dependency, "groupId", null);
final String artifactId = getMapValue(dependency, "artifactId", null);
final String version = getMapValue(dependency, "version", null);
versions.put(groupId + ":" + artifactId, version);
}
}
}
}
return versions;
}
public Set<String> getExclusionIds(final Collection<String> dependencyIds) {
final Set<String> exclusionIds = new LinkedHashSet<>();
for (final String dependencyId : dependencyIds) {
final int index1 = dependencyId.indexOf(':');
if (index1 != -1) {
final int index2 = dependencyId.indexOf(':', index1 + 1);
if (index2 != -1) {
final String exclusionId = dependencyId.substring(0, index2);
exclusionIds.add(exclusionId);
}
}
}
return exclusionIds;
}
public String getMavenDependencyId() {
return this.mavenDependencyId;
}
public String getMavenId() {
return this.mavenId;
}
public MavenRepository getMavenRepository() {
return this.mavenRepository;
}
public String getPackaging() {
return this.packaging;
}
public MavenPom getParentPom() {
if (this.parentPom == null) {
final MapEx parent = getProperty("parent");
if (parent == null) {
return null;
} else {
final String groupId = parent.getString("groupId");
final String artifactId = parent.getString("artifactId");
final String version = parent.getString("version");
this.parentPom = this.mavenRepository.getPom(groupId, artifactId, version);
if (this.parentPom == null) {
Logs.error(this, "Maven pom not found for parent " + groupId + ":" + artifactId + ":"
+ version + " in pom " + this);
}
}
}
return this.parentPom;
}
@Override
public MapEx getPomProperties() {
if (this.pomProperties == null) {
return MapEx.EMPTY;
} else {
if (this.mergedPomProperties == null) {
final MapEx properties = new LinkedHashMapEx();
final MavenPom parentPom = getParentPom();
if (parentPom != null) {
final MapEx parentProperties = parentPom.getPomProperties();
properties.putAll(parentProperties);
}
properties.putAll(this.pomProperties);
properties.put("project.artifactId", getArtifactId());
properties.put("project.version", getVersion());
properties.put("project.groupId", getGroupId());
this.mergedPomProperties = properties;
}
return this.mergedPomProperties;
}
}
public boolean isDependencyIgnored(final Set<String> dependencies, final String dependencyId) {
for (final String matchedDependencyId : dependencies) {
boolean match = true;
final String[] parts = dependencyId.split(":");
final String[] matchParts = matchedDependencyId.split(":");
if (matchParts.length == parts.length) {
for (int i = 0; i < parts.length - 2; i++) {
final String value1 = parts[i];
final String value2 = matchParts[i];
if (!value1.equals(value2)) {
match = false;
}
}
if (match) {
return true;
}
}
}
return false;
}
public ClassLoader newClassLoader() {
final String mavenId = getMavenId();
return this.mavenRepository.newClassLoader(mavenId);
}
public ClassLoader newClassLoader(final Collection<String> exclusionIds) {
final String mavenId = getMavenId();
return this.mavenRepository.newClassLoader(mavenId, exclusionIds);
}
public void setPackaging(final String packaging) {
this.packaging = packaging;
}
@Override
public String toString() {
return getMavenId();
}
protected void updateMavenId() {
final String groupId = getGroupId();
final String artifactId = getArtifactId();
final String classifier = getClassifier();
final String version = getVersion();
final String scope = getScope();
this.mavenId = MavenRepository.getMavenId(groupId, artifactId, this.packaging, classifier,
version, scope);
}
}