/* * Copyright 2012 James Moger * * Licensed 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.moxie; import java.io.Serializable; import java.lang.reflect.Field; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.moxie.utils.StringUtils; public class Pom implements Comparable<Pom>, Serializable { private static final long serialVersionUID = 1L; public String name; public String description; public String url; public String issuesUrl; public String organization; public String organizationUrl; public String inceptionYear; public String groupId; public String artifactId; public String version; public String classifier; public String packaging; public String parentGroupId; public String parentArtifactId; public String parentVersion; public String releaseVersion; public Date releaseDate; public String forumUrl; public String socialNetworkUrl; public String blogUrl; public String ciUrl; public String mavenUrl; public SCM scm; private final Map<String, String> properties; private final Map<Scope, List<Dependency>> dependencies; private final Map<String, String> managedVersions; private final Map<String, Scope> managedScopes; private final Set<String> exclusions; private final Map<String, String> antProperties; private final List<License> licenses; private final List<Person> developers; private final List<Person> contributors; public Pom() { version = ""; managedVersions = new TreeMap<String, String>(); managedScopes = new TreeMap<String, Scope>(); properties = new TreeMap<String, String>(); dependencies = new LinkedHashMap<Scope, List<Dependency>>(); exclusions = new TreeSet<String>(); antProperties = new TreeMap<String, String>(); licenses = new ArrayList<License>(); developers = new ArrayList<Person>(); contributors = new ArrayList<Person>(); scm = new SCM(); packaging = "jar"; } public void setAntProperties(Map<String, String> antProperties) { this.antProperties.putAll(antProperties); } public void setProperty(String key, String value) { if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) { return; } properties.put(key.trim(), value); } public String getAntProperty(String key) { if (antProperties.containsKey(key)) { return antProperties.get(key); } return null; } public Map<String, String> getProperties() { return properties; } private String getProperty(String key) { String value = null; if (properties.containsKey(key)) { value = properties.get(key); } if (StringUtils.isEmpty(value)) { if (key.startsWith("project.")) { // try reflection on project fields String fieldName = key.substring(key.indexOf('.') + 1); value = getFieldValue(fieldName); } else if (key.startsWith("parent.")) { // try reflection on project fields String fieldName = key.substring(key.indexOf('.') + 1); value = getFieldValue("parent" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1)); } else if (key.startsWith("env.")) { // Support all environment variables String env = key.substring(4); value = System.getenv().get(env); } } if (StringUtils.isEmpty(value)) { // Support all Ant properties if (antProperties.containsKey(key)) { value = antProperties.get(key); } } if (StringUtils.isEmpty(value)) { // Support all Java system properties value = System.getProperty(key); } if (StringUtils.isEmpty(value)) { System.out.println(MessageFormat.format("WARNING: property \"{0}\" not found for {1}", key, getCoordinates())); return key; } return value; } private String getFieldValue(String fieldName) { try { Field field = getClass().getField(fieldName); if (field == null) { return null; } field.setAccessible(true); Object o = field.get(this); if (o != null) { return o.toString(); } } catch (Exception e) { } return null; } public String getGroupId() { return groupId; } public String getArtifactId() { return artifactId; } public String getVersion() { return version; } public String getName() { return name; } public String getDescription() { return description; } public String getUrl() { return url; } public String getIssuesUrl() { return issuesUrl; } public SCM getScm() { return scm; } public String getOrganization() { return organization; } public void addLicense(License license) { licenses.add(license); } public List<License> getLicenses() { return licenses; } public void clearLicenses() { licenses.clear(); } public void addDeveloper(Person person) { developers.add(person); } public List<Person> getDevelopers() { return developers; } public void addContributor(Person person) { contributors.add(person); } public List<Person> getContributors() { return contributors; } public void addManagedDependency(Dependency dep, Scope scope) { addManagedDependency(dep, scope, true); } public void addManagedDependency(Dependency dep, Scope scope, boolean resolveProperties) { if (resolveProperties) { dep.groupId = resolveProperties(dep.groupId); dep.version = resolveProperties(dep.version); } if (dep.getManagementId().equals(getManagementId())) { System.out.println(MessageFormat.format("WARNING: ignoring circular managedDependency {0}", dep.getManagementId())); return; } if (!StringUtils.isEmpty(dep.extension)) { dep.extension = "jar"; } managedVersions.put(dep.getManagementId(), dep.version); if (scope != null) { managedScopes.put(dep.getManagementId(), scope); } } String getManagedVersion(Dependency dep) { if (managedVersions.containsKey(dep.getManagementId())) { return managedVersions.get(dep.getManagementId()); } return dep.version; } private Scope getManagedScope(Dependency dep) { if (managedScopes.containsKey(dep.getManagementId())) { return managedScopes.get(dep.getManagementId()); } return null; } public List<Scope> getScopes() { return new ArrayList<Scope>(dependencies.keySet()); } public void removeScope(Scope scope) { dependencies.remove(scope); } public void clearDependencies() { dependencies.clear(); } public boolean hasDependencies() { return dependencies.size() > 0; } public Scope addDependency(Dependency dep, Scope scope) { return addDependency(dep, scope, true); } public Scope addDependency(Dependency dep, Scope scope, boolean resolveProperties) { if (dep.isMavenObject()) { // determine group if (resolveProperties) { dep.groupId = resolveProperties(dep.groupId); } // determine version if (StringUtils.isEmpty(dep.version)) { dep.version = getManagedVersion(dep); } if (resolveProperties) { dep.version = resolveProperties(dep.version); } // set default extension, if unspecified if (StringUtils.isEmpty(dep.extension)) { dep.extension = "jar"; } if (dep.getManagementId().equals(getManagementId())) { System.out.println(MessageFormat.format("WARNING: ignoring circular dependency {0}", dep.getManagementId())); return null; } } else if ((dep instanceof SystemDependency)) { // System Dependency SystemDependency sys = (SystemDependency) dep; String path = resolveProperties(sys.path); dep = new SystemDependency(path); } // POM-level dependency exclusion is a Moxie feature if (hasDependency(dep) || excludes(dep)) { return null; } if (scope == null) { scope = getManagedScope(dep); // use default scope if completely unspecified if (scope == null) { scope = Scope.defaultScope; } } if (!dependencies.containsKey(scope)) { dependencies.put(scope, new ArrayList<Dependency>()); } dependencies.get(scope).add(dep); return scope; } void resolveProperties() { name = resolveProperties(name); description = resolveProperties(description); organization = resolveProperties(organization); url = resolveProperties(url); issuesUrl = resolveProperties(issuesUrl); } String resolveProperties(String string) { if (string == null) { return null; } Pattern p = Pattern.compile("\\$\\{[a-zA-Z0-9-_\\.]+\\}"); StringBuilder sb = new StringBuilder(string); int start = 0; while (true) { Matcher m = p.matcher(sb.toString()); if (m.find(start)) { String prop = m.group(); prop = prop.substring(2, prop.length() - 1); String value = getProperty(prop); if (value.equals(prop)) { // leave property intact, it will stand out start = m.end(); continue; } sb.replace(m.start(), m.end(), value); start = m.start() + value.length(); } else { return sb.toString(); } } } public List<Dependency> getDependencies(boolean ignoreDuplicates) { if (ignoreDuplicates) { // We just care about the unique dependencies Set<Dependency> uniques = new LinkedHashSet<Dependency>(); for (Scope dependencyScope : dependencies.keySet()) { uniques.addAll(getDependencies(dependencyScope)); } return new ArrayList<Dependency>(uniques); } else { // We care about all dependency objects, e.g. for alias resolution List<Dependency> all = new ArrayList<Dependency>(); for (Scope dependencyScope : dependencies.keySet()) { all.addAll(getDependencies(dependencyScope)); } return all; } } public List<Dependency> getDependencies(Scope scope) { return getDependencies(scope, Constants.RING1); } public List<Dependency> getDependencies(Scope scope, int ring) { Set<Dependency> set = new LinkedHashSet<Dependency>(); for (Scope dependencyScope : dependencies.keySet()) { Scope definedScope = dependencyScope; boolean includeScope = false; if (ring == Constants.RING1) { // project-specified dependency includeScope = scope.includeOnClasspath(dependencyScope); } else if (ring > Constants.RING1) { // transitive dependencies Scope transitiveScope = scope.getTransitiveScope(dependencyScope); includeScope = scope.includeOnClasspath(transitiveScope); definedScope = transitiveScope; } if (includeScope) { List<Dependency> list = dependencies.get(dependencyScope); for (Dependency dependency : list) { if (ring == Constants.RING1 && dependency.optional) { switch (scope) { case runtime: // optional dependencies are not exported continue; default: break; } } else if (ring > Constants.RING1 && dependency.optional) { // skip optional transitive dependencies continue; } dependency.ring = ring; dependency.definedScope = definedScope; set.add(dependency); } } } return new ArrayList<Dependency>(set); } public boolean hasParentDependency() { return !StringUtils.isEmpty(parentArtifactId); } public Dependency getParentDependency() { return new Dependency(parentGroupId + ":" + parentArtifactId + ":" + parentVersion + "::" + Constants.POM); } public boolean hasDependency(Dependency dependency) { String id = dependency.getMediationId(); for (Map.Entry<Scope, List<Dependency>> entry : dependencies.entrySet()) { for (Dependency dep : entry.getValue()) { if (dep.getMediationId().equals(id)) { return true; } } } return false; } /** * Maven POMs do not have a notion of a pom-level exclusion list. In Maven, * exclusions must be set within the dependency declaration. Because Moxie * supports direct dependency importing, Moxie also supports pom-level * exclusion. This method only makes sense for Moxie POMs. * * @param dependency * @return true of the dependency is excluded */ public boolean excludes(Dependency dependency) { return exclusions.contains(dependency.getMediationId()) || exclusions.contains(dependency.getManagementId()) || exclusions.contains(dependency.groupId); } /** * Maven POMs do not have a notion of a pom-level exclusion list. In Maven, * exclusions must be set within the dependency declaration. Because Moxie * supports direct dependency importing, Moxie also supports pom-level * exclusion. This method only makes sense for Moxie POMs. * * @param exclusions */ public void addExclusions(Collection<String> exclusions) { exclusions.addAll(exclusions); } public boolean isPOM() { return getExtension().equalsIgnoreCase(Constants.POM); } public boolean isJAR() { return getExtension().equalsIgnoreCase("jar"); } public boolean isWAR() { return getExtension().equalsIgnoreCase("war"); } public String getPackaging() { return packaging; } public String getExtension() { return Constants.getExtension(packaging); } public boolean isSnapshot() { if (version == null) { throw new MoxieException(MessageFormat.format("Version is undefined for \"{0}\"!", getCoordinates())); } return version.contains("-SNAPSHOT"); } public void inherit(Pom pom) { nonDestructiveCopy(pom.managedVersions, managedVersions); nonDestructiveCopy(pom.managedScopes, managedScopes); nonDestructiveCopy(pom.properties, properties); // inherit groupId and version from parent, by default // if parent definition is at end of pom then we already // have this data so ignore if (StringUtils.isEmpty(groupId)) { groupId = pom.groupId; } if (StringUtils.isEmpty(version)) { version = pom.version; } if (StringUtils.isEmpty(name)) { name = pom.name; } if (StringUtils.isEmpty(pom.description)) { description = pom.description; } if (pom.licenses != null) { licenses.addAll(pom.licenses); } if (pom.developers!= null) { developers.addAll(pom.developers); } if (pom.contributors != null) { contributors.addAll(pom.contributors); } if (StringUtils.isEmpty(organization)) { organization = pom.organization; } if (StringUtils.isEmpty(url)) { url = pom.url; } if (StringUtils.isEmpty(issuesUrl)) { issuesUrl = pom.issuesUrl; } } public void importManagedDependencies(Pom pom) { nonDestructiveCopy(pom.managedVersions, managedVersions); nonDestructiveCopy(pom.managedScopes, managedScopes); } /** * Copies values from sourceMap into destinationMap without overriding keys * already in destinationMap. * * @param sourceMap * @param destinationMap */ private <K> void nonDestructiveCopy(Map<String, K> sourceMap, Map<String, K> destinationMap) { Set<String> sourceKeys = new HashSet<String>(sourceMap.keySet()); sourceKeys.removeAll(destinationMap.keySet()); for (String key : sourceKeys) { destinationMap.put(key, sourceMap.get(key)); } } public String getManagementId() { return groupId + ":" + artifactId; } public String getCoordinates() { return groupId + ":" + artifactId + ":" + version + (classifier == null ? "" : (":" + classifier)); } public String getPrefix() { String [] chunks = groupId.split("\\."); if (chunks.length < 2) { // single path return "/" + chunks[0]; } else { // add first two paths return "/" + chunks[0] + "/" + chunks[1]; } } @Override public String toString() { return getCoordinates(); } public String toXML() { return toXML(true); } public String toXML(boolean includeProperties) { return toXML(includeProperties, new ArrayList<RemoteRepository>()); } public String toXML(boolean includeProperties, Collection<RemoteRepository> repositories) { String pomVersion = "4.0.0"; StringBuilder sb = new StringBuilder(); sb.append(MessageFormat.format("<project xmlns=\"http://maven.apache.org/POM/{0}\" ", pomVersion)); sb.append("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"); sb.append(MessageFormat.format("xsi:schemaLocation=\"http://maven.apache.org/POM/{0} ", pomVersion)); sb.append(MessageFormat.format("http://maven.apache.org/maven-v{0}.xsd\">\n", pomVersion.replace('.', '_'))); sb.append('\n'); sb.append(StringUtils.toXML("modelVersion", pomVersion)); sb.append('\n'); // parent metadata if (hasParentDependency()) { StringBuilder node = new StringBuilder(); node.append("<parent>\n"); node.append(StringUtils.toXML("groupId", parentGroupId)); node.append(StringUtils.toXML("artifactId", parentArtifactId)); node.append(StringUtils.toXML("version", parentVersion)); node.append("</parent>\n"); sb.append(StringUtils.insertHalfTab(node.toString())); sb.append('\n'); } // project metadata sb.append(StringUtils.toXML("groupId", groupId)); sb.append(StringUtils.toXML("artifactId", artifactId)); sb.append(StringUtils.toXML("version", version)); sb.append(StringUtils.toXML("packaging", packaging)); sb.append(StringUtils.toXML("name", name)); sb.append(StringUtils.toXML("description", description)); String org = StringUtils.toXML("name", organization).trim() + StringUtils.toXML("url", organizationUrl).trim(); sb.append(StringUtils.toXML("organization", org)); sb.append(StringUtils.toXML("url", url)); sb.append(StringUtils.toXML("inceptionYear", inceptionYear)); sb.append('\n'); // licenses if (licenses.size() > 0) { StringBuilder node = new StringBuilder(); node.append("<licenses>\n"); for (License license : licenses) { node.append(StringUtils.insertHalfTab(license.toXML())); } node.append("</licenses>\n"); sb.append(StringUtils.insertHalfTab(node.toString())); sb.append('\n'); } // scm if (!scm.isEmpty()) { sb.append(StringUtils.insertHalfTab(scm.toXML())); sb.append('\n'); } // persons if (developers.size() > 0) { sb.append(StringUtils.insertHalfTab(toXML("developer", developers))); sb.append('\n'); } if (contributors.size() > 0) { sb.append(StringUtils.insertHalfTab(toXML("contributor", contributors))); sb.append('\n'); } // properties if (includeProperties && properties.size() > 0) { Map<String, String> filtered = new LinkedHashMap<String, String>(); for (Map.Entry<String, String> entry : properties.entrySet()) { String key = entry.getKey(); // strip curly brace notation if (key.startsWith("${")) { key = key.substring(2); } if (key.endsWith("}")) { key = key.substring(0, key.length() - 1); } // skip project.* keys if (!key.toLowerCase().startsWith("project.")) { filtered.put(key, entry.getValue()); } } // only output filtered properties if (filtered.size() > 0) { StringBuilder node = new StringBuilder(); node.append("<properties>\n"); for (Map.Entry<String, String> entry : filtered.entrySet()) { node.append(StringUtils.toXML(entry.getKey(), entry.getValue())); } node.append("</properties>\n"); sb.append(StringUtils.insertHalfTab(node.toString())); sb.append('\n'); } } // repositories if (repositories.size() > 0) { StringBuilder node = new StringBuilder(); node.append("<repositories>\n"); StringBuilder subnode = new StringBuilder(); for (RemoteRepository repository : repositories) { subnode.append(repository.toXML()); } node.append(StringUtils.insertHalfTab(subnode.toString())); node.append("</repositories>\n"); sb.append(StringUtils.insertHalfTab(node.toString())); sb.append('\n'); } // managed versions if (managedVersions.size() > 0) { StringBuilder node = new StringBuilder(); node.append("<dependencyManagement>\n"); node.append("<dependencies>\n"); StringBuilder subnode = new StringBuilder(); for (Map.Entry<String, String> entry : managedVersions.entrySet()) { String key = entry.getKey(); String version = entry.getValue(); Scope scope = managedScopes.get(key); Dependency dep = new Dependency(key + ":" + version); subnode.append(dep.toXML(scope)); } node.append(StringUtils.insertHalfTab(subnode.toString())); node.append("</dependencies>\n"); node.append("</dependencyManagement>\n"); sb.append(StringUtils.insertHalfTab(node.toString())); sb.append('\n'); } // dependencies if (dependencies.size() > 0) { StringBuilder node = new StringBuilder(); node.append("<dependencies>\n"); for (Map.Entry<Scope, List<Dependency>> entry : dependencies.entrySet()) { Scope scope = entry.getKey(); if (!scope.isMavenScope()) { // skip non-Maven scopes continue; } node.append(MessageFormat.format("\t<!-- {0} dependencies -->\n", scope.name())); for (Dependency dependency : entry.getValue()) { StringBuilder depNode = new StringBuilder(); depNode.append(dependency.toXML(entry.getKey())); node.append(StringUtils.insertHalfTab(depNode.toString())); } } node.append("</dependencies>\n"); sb.append(StringUtils.insertHalfTab(node.toString())); sb.append('\n'); } // close project sb.append("</project>\n\n"); return sb.toString(); } private String toXML(String nodename, List<Person> persons) { StringBuilder list = new StringBuilder(); if (persons.size() > 0) { list.append(MessageFormat.format("<{0}s>\n", nodename)); for (Person person : persons) { list.append(StringUtils.insertHalfTab(person.toXML(nodename))); } list.append(MessageFormat.format("</{0}s>\n", nodename)); } return list.toString(); } @Override public int compareTo(Pom o) { int managementId = getManagementId().compareTo(o.getManagementId()); if (managementId == 0) { // same artifact, sort by version ArtifactVersion v1 = new ArtifactVersion(version); ArtifactVersion v2 = new ArtifactVersion(o.version); return v1.compareTo(v2); } return managementId; } }