blob: 3eda682ebeb410f817a5d01bc2fd0c06ec012179 [file] [log] [blame]
Stuart McCullochbb014372012-06-07 21:57:32 +00001package aQute.bnd.maven;
2
3import java.io.*;
4import java.util.*;
5
6import javax.xml.parsers.*;
7import javax.xml.xpath.*;
8
9import org.w3c.dom.*;
10
Stuart McCulloch39cc9ac2012-07-16 13:43:38 +000011import aQute.bnd.osgi.*;
Stuart McCullochbb014372012-06-07 21:57:32 +000012import aQute.lib.io.*;
Stuart McCullochbb014372012-06-07 21:57:32 +000013
14/**
Stuart McCulloch2286f232012-06-15 13:27:53 +000015 * Provides a way to parse a maven pom as properties. This provides most of the
16 * maven elements as properties. It also provides
17 * pom.scope.[compile|test|runtime|provided|system] properties that can be
18 * appended to the build and run path. That is, they are in the correct format
19 * for this.
Stuart McCullochbb014372012-06-07 21:57:32 +000020 */
21public class PomParser extends Processor {
22 static DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
23 static XPathFactory xpathf = XPathFactory.newInstance();
24 static Set<String> multiple = new HashSet<String>();
Stuart McCulloch2286f232012-06-15 13:27:53 +000025 static Set<String> skip = new HashSet<String>();
Stuart McCullochbb014372012-06-07 21:57:32 +000026
27 static {
28 dbf.setNamespaceAware(false);
Stuart McCulloch2286f232012-06-15 13:27:53 +000029
Stuart McCullochbb014372012-06-07 21:57:32 +000030 // Set all elements that need enumeration of their elements
31 // these will not use the name of the subelement but instead
32 // they use an index from 0..n
33 multiple.add("mailingLists");
34 multiple.add("pluginRepositories");
35 multiple.add("repositories");
36 multiple.add("resources");
37 multiple.add("executions");
38 multiple.add("goals");
39 multiple.add("includes");
40 multiple.add("excludes");
41
42 // These properties are not very useful and
43 // pollute the property space.
44 skip.add("plugins");
45 skip.add("dependencies");
46 skip.add("reporting");
47 skip.add("extensions");
Stuart McCulloch2286f232012-06-15 13:27:53 +000048
Stuart McCullochbb014372012-06-07 21:57:32 +000049 }
50
51 public Properties getProperties(File pom) throws Exception {
52 DocumentBuilder db = dbf.newDocumentBuilder();
53 XPath xpath = xpathf.newXPath();
54 pom = pom.getAbsoluteFile();
55 Document doc = db.parse(pom);
56 Properties p = new Properties();
57
58 // Check if there is a parent pom
59 String relativePath = xpath.evaluate("project/parent/relativePath", doc);
Stuart McCulloch2286f232012-06-15 13:27:53 +000060 if (relativePath != null && relativePath.length() != 0) {
Stuart McCullochbb014372012-06-07 21:57:32 +000061 File parentPom = IO.getFile(pom.getParentFile(), relativePath);
62 if (parentPom.isFile()) {
63 Properties parentProps = getProperties(parentPom);
64 p.putAll(parentProps);
65 } else {
66 error("Parent pom for %s is not an existing file (could be directory): %s", pom, parentPom);
67 }
68 }
69
70 Element e = doc.getDocumentElement();
71 traverse("pom", e, p);
72
Stuart McCulloch2286f232012-06-15 13:27:53 +000073 String scopes[] = {
74 "provided", "runtime", "test", "system"
75 };
Stuart McCullochbb014372012-06-07 21:57:32 +000076 NodeList set = (NodeList) xpath.evaluate("//dependency[not(scope) or scope='compile']", doc,
77 XPathConstants.NODESET);
78 if (set.getLength() != 0)
79 p.put("pom.scope.compile", toBsn(set));
80
81 for (String scope : scopes) {
Stuart McCulloch2286f232012-06-15 13:27:53 +000082 set = (NodeList) xpath.evaluate("//dependency[scope='" + scope + "']", doc, XPathConstants.NODESET);
Stuart McCullochbb014372012-06-07 21:57:32 +000083 if (set.getLength() != 0)
84 p.put("pom.scope." + scope, toBsn(set));
85 }
86
87 return p;
88 }
89
90 private Object toBsn(NodeList set) throws XPathExpressionException {
91 XPath xpath = xpathf.newXPath();
92 StringBuilder sb = new StringBuilder();
93 String del = "";
94 for (int i = 0; i < set.getLength(); i++) {
95 Node child = set.item(i);
96 String version = xpath.evaluate("version", child);
97 sb.append(del);
98 sb.append(xpath.evaluate("groupId", child));
99 sb.append(".");
100 sb.append(xpath.evaluate("artifactId", child));
Stuart McCulloch2286f232012-06-15 13:27:53 +0000101 if (version != null && version.trim().length() != 0) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000102 sb.append(";version=");
Stuart McCulloch2286f232012-06-15 13:27:53 +0000103 sb.append(Analyzer.cleanupVersion(version));
Stuart McCullochbb014372012-06-07 21:57:32 +0000104 }
105 del = ", ";
106 }
107 return sb.toString();
108 }
109
110 /**
Stuart McCulloch2286f232012-06-15 13:27:53 +0000111 * The maven POM is quite straightforward, it is basically a structured
112 * property file.
113 *
Stuart McCullochbb014372012-06-07 21:57:32 +0000114 * @param name
115 * @param parent
116 * @param p
117 */
118 static void traverse(String name, Node parent, Properties p) {
Stuart McCulloch2286f232012-06-15 13:27:53 +0000119 if (skip.contains(parent.getNodeName()))
Stuart McCullochbb014372012-06-07 21:57:32 +0000120 return;
Stuart McCulloch2286f232012-06-15 13:27:53 +0000121
Stuart McCullochbb014372012-06-07 21:57:32 +0000122 NodeList children = parent.getChildNodes();
123 if (multiple.contains(parent.getNodeName())) {
124 int n = 0;
125 for (int i = 0; i < children.getLength(); i++) {
126 Node child = children.item(i);
127 if (!(child instanceof Text)) {
128
129 traverse(name + "." + n++, child, p);
130 }
131 }
132 } else {
133 for (int i = 0; i < children.getLength(); i++) {
134 Node child = children.item(i);
135 if (child instanceof Text) {
136 String value = child.getNodeValue().trim();
Stuart McCulloch2286f232012-06-15 13:27:53 +0000137 if (value.length() != 0) {
Stuart McCullochbb014372012-06-07 21:57:32 +0000138 p.put(name, value);
139 }
140 } else {
141 traverse(name + "." + child.getNodeName(), child, p);
142 }
143 }
144 }
145 }
146}