blob: 2244fd218b715e30ac6889ec26e01b1535ae5fbe [file] [log] [blame]
Stuart McCullochf3173222012-06-07 21:57:32 +00001package aQute.lib.osgi.eclipse;
2
3import java.io.*;
4import java.util.*;
5import java.util.regex.*;
6
7import javax.xml.parsers.*;
8
9import org.w3c.dom.*;
10import org.xml.sax.*;
11
12import aQute.libg.reporter.*;
13
14/**
15 * Parse the Eclipse project information for the classpath. Unfortunately, it is
16 * impossible to read the variables. They are ignored but that can cause
17 * problems.
18 *
19 * @version $Revision$
20 */
21public class EclipseClasspath {
Stuart McCulloch4482c702012-06-15 13:27:53 +000022 static DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
23 DocumentBuilder db;
24 File project;
25 File workspace;
26 Set<File> sources = new LinkedHashSet<File>();
27 Set<File> allSources = new LinkedHashSet<File>();
Stuart McCullochf3173222012-06-07 21:57:32 +000028
Stuart McCulloch4482c702012-06-15 13:27:53 +000029 Set<File> classpath = new LinkedHashSet<File>();
30 List<File> dependents = new ArrayList<File>();
31 File output;
32 boolean recurse = true;
33 Set<File> exports = new LinkedHashSet<File>();
34 Map<String,String> properties = new HashMap<String,String>();
35 Reporter reporter;
36 int options;
37 Set<File> bootclasspath = new LinkedHashSet<File>();
Stuart McCullochf3173222012-06-07 21:57:32 +000038
Stuart McCulloch4482c702012-06-15 13:27:53 +000039 public final static int DO_VARIABLES = 1;
Stuart McCullochf3173222012-06-07 21:57:32 +000040
Stuart McCulloch4482c702012-06-15 13:27:53 +000041 /**
42 * Parse an Eclipse project structure to discover the classpath.
43 *
44 * @param workspace
45 * Points to workspace
46 * @param project
47 * Points to project
48 * @throws ParserConfigurationException
49 * @throws SAXException
50 * @throws IOException
51 */
Stuart McCullochf3173222012-06-07 21:57:32 +000052
Stuart McCulloch4482c702012-06-15 13:27:53 +000053 public EclipseClasspath(Reporter reporter, File workspace, File project, int options) throws Exception {
54 this.project = project.getCanonicalFile();
55 this.workspace = workspace.getCanonicalFile();
56 this.reporter = reporter;
57 db = documentBuilderFactory.newDocumentBuilder();
58 parse(this.project, true);
59 db = null;
60 }
Stuart McCullochf3173222012-06-07 21:57:32 +000061
Stuart McCulloch4482c702012-06-15 13:27:53 +000062 public EclipseClasspath(Reporter reporter, File workspace, File project) throws Exception {
63 this(reporter, workspace, project, 0);
64 }
Stuart McCullochf3173222012-06-07 21:57:32 +000065
Stuart McCulloch4482c702012-06-15 13:27:53 +000066 /**
67 * Recursive routine to parse the files. If a sub project is detected, it is
68 * parsed before the parsing continues. This should give the right order.
69 *
70 * @param project
71 * Project directory
72 * @param top
73 * If this is the top project
74 * @throws ParserConfigurationException
75 * @throws SAXException
76 * @throws IOException
77 */
78 void parse(File project, boolean top) throws ParserConfigurationException, SAXException, IOException {
79 File file = new File(project, ".classpath");
80 if (!file.exists())
81 throw new FileNotFoundException(".classpath file not found: " + file.getAbsolutePath());
Stuart McCullochf3173222012-06-07 21:57:32 +000082
Stuart McCulloch4482c702012-06-15 13:27:53 +000083 Document doc = db.parse(file);
84 NodeList nodelist = doc.getDocumentElement().getElementsByTagName("classpathentry");
Stuart McCullochf3173222012-06-07 21:57:32 +000085
Stuart McCulloch4482c702012-06-15 13:27:53 +000086 if (nodelist == null)
87 throw new IllegalArgumentException("Can not find classpathentry in classpath file");
Stuart McCullochf3173222012-06-07 21:57:32 +000088
Stuart McCulloch4482c702012-06-15 13:27:53 +000089 for (int i = 0; i < nodelist.getLength(); i++) {
90 Node node = nodelist.item(i);
91 NamedNodeMap attrs = node.getAttributes();
92 String kind = get(attrs, "kind");
93 if ("src".equals(kind)) {
94 String path = get(attrs, "path");
95 // TODO boolean exported = "true".equalsIgnoreCase(get(attrs,
96 // "exported"));
97 if (path.startsWith("/")) {
98 // We have another project
99 File subProject = getFile(workspace, project, path);
100 if (recurse)
101 parse(subProject, false);
102 dependents.add(subProject.getCanonicalFile());
103 } else {
104 File src = getFile(workspace, project, path);
105 allSources.add(src);
106 if (top) {
107 // We only want the sources for our own project
108 // or we'll compile all at once. Not a good idea
109 // because project settings can differ.
110 sources.add(src);
111 }
112 }
113 } else if ("lib".equals(kind)) {
114 String path = get(attrs, "path");
115 boolean exported = "true".equalsIgnoreCase(get(attrs, "exported"));
116 if (top || exported) {
117 File jar = getFile(workspace, project, path);
118 if (jar.getName().startsWith("ee."))
119 bootclasspath.add(jar);
120 else
121 classpath.add(jar);
122 if (exported)
123 exports.add(jar);
124 }
125 } else if ("output".equals(kind)) {
126 String path = get(attrs, "path");
127 path = path.replace('/', File.separatorChar);
128 output = getFile(workspace, project, path);
129 classpath.add(output);
130 exports.add(output);
131 } else if ("var".equals(kind)) {
132 boolean exported = "true".equalsIgnoreCase(get(attrs, "exported"));
133 File lib = replaceVar(get(attrs, "path"));
134 File slib = replaceVar(get(attrs, "sourcepath"));
135 if (lib != null) {
136 classpath.add(lib);
137 if (exported)
138 exports.add(lib);
139 }
140 if (slib != null)
141 sources.add(slib);
142 } else if ("con".equals(kind)) {
143 // Should do something useful ...
144 }
145 }
146 }
Stuart McCullochf3173222012-06-07 21:57:32 +0000147
Stuart McCulloch4482c702012-06-15 13:27:53 +0000148 private File getFile(File abs, File relative, String opath) {
149 String path = opath.replace('/', File.separatorChar);
150 File result = new File(path);
151 if (result.isAbsolute() && result.isFile()) {
152 return result;
153 }
154 if (path.startsWith(File.separator)) {
155 result = abs;
156 path = path.substring(1);
157 } else
158 result = relative;
Stuart McCullochf3173222012-06-07 21:57:32 +0000159
Stuart McCulloch4482c702012-06-15 13:27:53 +0000160 StringTokenizer st = new StringTokenizer(path, File.separator);
161 while (st.hasMoreTokens()) {
162 String token = st.nextToken();
163 result = new File(result, token);
164 }
Stuart McCullochf3173222012-06-07 21:57:32 +0000165
Stuart McCulloch4482c702012-06-15 13:27:53 +0000166 if (!result.exists())
167 System.err.println("File not found: project=" + project + " workspace=" + workspace + " path=" + opath
168 + " file=" + result);
169 return result;
170 }
Stuart McCullochf3173222012-06-07 21:57:32 +0000171
Stuart McCulloch4482c702012-06-15 13:27:53 +0000172 static Pattern PATH = Pattern.compile("([A-Z_]+)/(.*)");
Stuart McCullochf3173222012-06-07 21:57:32 +0000173
Stuart McCulloch4482c702012-06-15 13:27:53 +0000174 private File replaceVar(String path) {
175 if ((options & DO_VARIABLES) == 0)
176 return null;
Stuart McCullochf3173222012-06-07 21:57:32 +0000177
Stuart McCulloch4482c702012-06-15 13:27:53 +0000178 Matcher m = PATH.matcher(path);
179 if (m.matches()) {
180 String var = m.group(1);
181 String remainder = m.group(2);
182 String base = properties.get(var);
183 if (base != null) {
184 File b = new File(base);
185 File f = new File(b, remainder.replace('/', File.separatorChar));
186 return f;
187 } else
188 reporter.error("Can't find replacement variable for: " + path);
189 } else
190 reporter.error("Cant split variable path: " + path);
191 return null;
192 }
Stuart McCullochf3173222012-06-07 21:57:32 +0000193
Stuart McCulloch4482c702012-06-15 13:27:53 +0000194 private String get(NamedNodeMap map, String name) {
195 Node node = map.getNamedItem(name);
196 if (node == null)
197 return null;
Stuart McCullochf3173222012-06-07 21:57:32 +0000198
Stuart McCulloch4482c702012-06-15 13:27:53 +0000199 return node.getNodeValue();
200 }
Stuart McCullochf3173222012-06-07 21:57:32 +0000201
Stuart McCulloch4482c702012-06-15 13:27:53 +0000202 public Set<File> getClasspath() {
203 return classpath;
204 }
Stuart McCullochf3173222012-06-07 21:57:32 +0000205
Stuart McCulloch4482c702012-06-15 13:27:53 +0000206 public Set<File> getSourcepath() {
207 return sources;
208 }
Stuart McCullochf3173222012-06-07 21:57:32 +0000209
Stuart McCulloch4482c702012-06-15 13:27:53 +0000210 public File getOutput() {
211 return output;
212 }
Stuart McCullochf3173222012-06-07 21:57:32 +0000213
Stuart McCulloch4482c702012-06-15 13:27:53 +0000214 public List<File> getDependents() {
215 return dependents;
216 }
Stuart McCullochf3173222012-06-07 21:57:32 +0000217
Stuart McCulloch4482c702012-06-15 13:27:53 +0000218 public void setRecurse(boolean recurse) {
219 this.recurse = recurse;
220 }
Stuart McCullochf3173222012-06-07 21:57:32 +0000221
Stuart McCulloch4482c702012-06-15 13:27:53 +0000222 public Set<File> getExports() {
223 return exports;
224 }
Stuart McCullochf3173222012-06-07 21:57:32 +0000225
Stuart McCulloch4482c702012-06-15 13:27:53 +0000226 public void setProperties(Map<String,String> map) {
227 this.properties = map;
228 }
Stuart McCullochf3173222012-06-07 21:57:32 +0000229
Stuart McCulloch4482c702012-06-15 13:27:53 +0000230 public Set<File> getBootclasspath() {
231 return bootclasspath;
232 }
233
234 public Set<File> getAllSources() {
235 return allSources;
236 }
Stuart McCullochf3173222012-06-07 21:57:32 +0000237
238}