blob: 0f8a332d8c82323187a4c7327f6a5e58d63bbf13 [file] [log] [blame]
Richard S. Hall85bafab2009-07-13 13:25:46 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20package org.cauldron.bld.config;
21
22import java.net.URI;
23import java.util.ArrayList;
24import java.util.Collections;
25import java.util.List;
26import java.util.Map;
27import java.util.Properties;
28import java.util.TreeMap;
29import java.util.TreeSet;
30import java.util.regex.Matcher;
31import java.util.regex.Pattern;
32
33import org.cauldron.bld.config.IBldProject.IBldBundle;
34import org.cauldron.bld.core.BldCore;
35import org.cauldron.bld.core.internal.model.eclipse.SigilBundle;
36import org.cauldron.bld.core.internal.model.osgi.BundleModelElement;
37import org.cauldron.sigil.model.common.VersionRange;
38import org.cauldron.sigil.model.eclipse.ISCAComposite;
39import org.cauldron.sigil.model.eclipse.ISigilBundle;
40import org.cauldron.sigil.model.osgi.IBundleModelElement;
41import org.cauldron.sigil.model.osgi.IPackageExport;
42import org.cauldron.sigil.model.osgi.IPackageImport;
43import org.cauldron.sigil.model.osgi.IRequiredBundle;
44import org.eclipse.core.runtime.IPath;
45import org.eclipse.core.runtime.Path;
46import org.osgi.framework.Version;
47
48import aQute.lib.osgi.Constants;
49
50public class BldConverter {
51 private static final String classpathFormat = "<classpathentry kind=\"%s\" path=\"%s\"/>";
52 private BldConfig config;
53 private Properties packageDefaults;
54 private TreeSet<String> packageWildDefaults;
55
56 public BldConverter(BldConfig config) {
57 this.config = config;
58 }
59
60 /**
61 * converts to an ISigilBundle.
62 * @param id
63 * @param bundle
64 * @return
65 */
66 public ISigilBundle getBundle(String id, IBldBundle bundle) {
67
68 ISigilBundle sigilBundle = new SigilBundle();
69 IBundleModelElement info = new BundleModelElement();
70 sigilBundle.setBundleInfo(info);
71
72 // exports
73 // FIXME: UI doesn't understand export wildcard packages
74 for (IPackageExport export : bundle.getExports()) {
75 IPackageExport clone = (IPackageExport) export.clone();
76 clone.setParent(null);
77 info.addExport(clone);
78 }
79
80 // imports
81 for (IPackageImport import1 : bundle.getImports()) {
82 IPackageImport clone = (IPackageImport) import1.clone();
83 clone.setParent(null);
84 info.addImport(clone);
85 }
86
87 // requires
88 for (IRequiredBundle require : bundle.getRequires()) {
89 IRequiredBundle clone = (IRequiredBundle) require.clone();
90 clone.setParent(null);
91 info.addRequiredBundle(clone);
92 }
93
94 // fragment
95 IRequiredBundle fragment = bundle.getFragmentHost();
96 if (fragment != null) {
97 info.setFragmentHost(fragment);
98 }
99
100 // contents
101 for (String pkg : bundle.getContents()) {
102 sigilBundle.addPackage(pkg);
103 }
104
105 // downloads
106 for (String pkg : bundle.getDownloadContents()) {
107 sigilBundle.addDownloadPackage(pkg);
108 }
109
110 // sources
111 for (String source : config.getList(null, BldConfig.L_SRC_CONTENTS) ) {
112 sigilBundle.addClasspathEntry(String.format(classpathFormat, "src", source));
113 }
114
115 // libs
116 Map<String, Map<String, String>> libs = bundle.getLibs();
117
118 for (String path : libs.keySet()) {
119 Map<String, String> attr = libs.get(path);
120 String kind = attr.get(BldAttr.KIND_ATTRIBUTE);
121 String publish = attr.get(BldAttr.PUBLISH_ATTRIBUTE);
122
123 if (publish != null) {
124 // FIXME: UI doesn't understand publish=name
125 BldCore.error("Can't convert -libs publish=" + publish);
126 continue;
127 }
128
129 if ("classpath".equals(kind)) {
130 sigilBundle.addClasspathEntry(String.format(classpathFormat, "lib", path));
131 } else {
132 BldCore.error("Can't convert -libs kind=" + kind);
133 }
134 }
135
136 // resources
137 // FIXME: UI doesn't support -resources: path1=path2
138 Map<String, String> resources = bundle.getResources();
139 for (String resource : resources.keySet()) {
140 String fsPath = resources.get(resource);
141 if (!"".equals(fsPath)) {
142 BldCore.error("FIXME: can't convert resource: " + resource + "=" + fsPath);
143 }
144 sigilBundle.addSourcePath(new Path(resource));
145 }
146
147 ////////////////////
148 // simple headers
149
150 info.setSymbolicName(bundle.getSymbolicName());
151
152 info.setVersion(Version.parseVersion(bundle.getVersion()));
153
154 String activator = bundle.getActivator();
155 if (activator != null)
156 info.setActivator(activator);
157
158 Properties headers = config.getProps(id, BldConfig.P_HEADER);
159 String header;
160
161 header = headers.getProperty("CATEGORY");
162 if (header != null)
163 info.setCategory(header);
164
165 header = headers.getProperty(Constants.BUNDLE_CONTACTADDRESS);
166 if (header != null)
167 info.setContactAddress(header);
168
169 header = headers.getProperty(Constants.BUNDLE_COPYRIGHT);
170 if (header != null)
171 info.setCopyright(header);
172
173 header = headers.getProperty(Constants.BUNDLE_DESCRIPTION);
174 if (header != null)
175 info.setDescription(header);
176
177 header = headers.getProperty(Constants.BUNDLE_VENDOR);
178 if (header != null)
179 info.setVendor(header);
180
181 header = headers.getProperty(Constants.BUNDLE_NAME);
182 if (header != null)
183 info.setName(header);
184
185 header = headers.getProperty(Constants.BUNDLE_DOCURL);
186 if (header != null)
187 info.setDocURI(URI.create(header));
188
189 header = headers.getProperty(Constants.BUNDLE_LICENSE);
190 if (header != null)
191 info.setDocURI(URI.create(header));
192
193 return sigilBundle;
194 }
195
196 private VersionRange defaultVersion(VersionRange current, String defaultRange) {
197 if (current.equals(VersionRange.ANY_VERSION) ||
198 current.equals(VersionRange.parseVersionRange(defaultRange))) {
199 return null;
200 }
201 return current;
202 }
203
204 // FIXME - copied from BldProject
205 private String getDefaultPackageVersion(String name) {
206 if (packageDefaults == null) {
207 packageDefaults = config.getProps(null, BldConfig.P_PACKAGE_VERSION);
208 packageWildDefaults = new TreeSet<String>();
209
210 for (Object key : packageDefaults.keySet()) {
211 String pkg = (String)key;
212 if (pkg.endsWith("*")) {
213 packageWildDefaults.add(pkg.substring(0, pkg.length() - 1));
214 }
215 }
216 }
217
218 String version = packageDefaults.getProperty(name);
219
220 if (version == null) {
221 for (String pkg : packageWildDefaults) {
222 if (name.startsWith(pkg)) {
223 version = packageDefaults.getProperty(pkg + "*");
224 // break; -- don't break, as we want the longest match
225 }
226 }
227 }
228
229 return version;
230 }
231
232
233 /**
234 * converts from an ISigilBundle.
235 *
236 * @param id
237 * @param bundle
238 */
239 public void setBundle(String id, ISigilBundle bundle) {
240 IBundleModelElement info = bundle.getBundleInfo();
241 String bundleVersion = config.getString(id, BldConfig.S_VERSION);
242
243 // exports
244 Map<String, Map<String, String>> exports = new TreeMap<String, Map<String,String>>();
245 for (IPackageExport export : info.getExports()) {
246 Map<String, String> map2 = new TreeMap<String, String>();
247 String version = export.getVersion().toString();
248 if (!version.equals(bundleVersion))
249 map2.put(BldAttr.VERSION_ATTRIBUTE, version);
250 exports.put(export.getPackageName(), map2);
251 }
252
253 if (!exports.isEmpty() || !config.getMap(id, BldConfig.M_EXPORTS).isEmpty()) {
254 config.setMap(id, BldConfig.M_EXPORTS, exports);
255 }
256
257 // imports
258 Map<String, Map<String, String>> imports = new TreeMap<String, Map<String,String>>();
259
260 // FIXME: default version logic is wrong here
261 // if the version to be saved is the same as the default version,
262 // then we should _remove_ the version from the value being saved,
263 // since config.getMap() does not apply default versions.
264 for (IPackageImport import1 : info.getImports()) {
265 Map<String, String> map2 = new TreeMap<String, String>();
266 String name = import1.getPackageName();
267 VersionRange versions = defaultVersion(import1.getVersions(), getDefaultPackageVersion(name));
268
269 boolean isDependency = import1.isDependency();
270 Map<String, String> selfImport = exports.get(name);
271
272 if (selfImport != null) {
273 // avoid saving self-import attributes, e.g.
274 // org.cauldron.newton.example.fractal.engine;resolve=auto;version=1.0.0
275 isDependency = true;
276
277 if (versions != null) {
278 String exportVersion = selfImport.get(BldAttr.VERSION_ATTRIBUTE);
279 if (exportVersion == null)
280 exportVersion = bundleVersion;
281
282 if (exportVersion.equals(versions.toString())) {
283 versions = null;
284 }
285 }
286 }
287
288 if (versions != null) {
289 map2.put(BldAttr.VERSION_ATTRIBUTE, versions.toString());
290 }
291
292 if (import1.isOptional()) {
293 map2.put(BldAttr.RESOLUTION_ATTRIBUTE, BldAttr.RESOLUTION_OPTIONAL);
294 }
295
296 String resolve = BldProject.getResolve(import1, isDependency);
297 if (resolve != null)
298 map2.put(BldAttr.RESOLVE_ATTRIBUTE, resolve);
299
300 imports.put(name, map2);
301 }
302 if (!imports.isEmpty() || !config.getMap(id, BldConfig.M_IMPORTS).isEmpty()) {
303 config.setMap(id, BldConfig.M_IMPORTS, imports);
304 }
305
306 // requires
307 Properties defaultBundles = config.getProps(null, BldConfig.P_BUNDLE_VERSION);
308 Map<String, Map<String, String>> requires = new TreeMap<String, Map<String,String>>();
309
310 for (IRequiredBundle require : info.getRequiredBundles()) {
311 Map<String, String> map2 = new TreeMap<String, String>();
312 String name = require.getSymbolicName();
313 VersionRange versions = defaultVersion(require.getVersions(), defaultBundles.getProperty(name));
314 if (versions != null)
315 map2.put(BldAttr.VERSION_ATTRIBUTE, versions.toString());
316 requires.put(name, map2);
317 }
318 if (!requires.isEmpty() || !config.getMap(id, BldConfig.M_REQUIRES).isEmpty()) {
319 config.setMap(id, BldConfig.M_REQUIRES, requires);
320 }
321
322 // fragment
323 Map<String, Map<String, String>> fragments = new TreeMap<String, Map<String,String>>();
324 IRequiredBundle fragment = info.getFragmentHost();
325 if (fragment != null) {
326 Map<String, String> map2 = new TreeMap<String, String>();
327 String name = fragment.getSymbolicName();
328 VersionRange versions = defaultVersion(fragment.getVersions(), defaultBundles.getProperty(name));
329 if (versions != null)
330 map2.put(BldAttr.VERSION_ATTRIBUTE, versions.toString());
331 fragments.put(name, map2);
332 }
333 if (!fragments.isEmpty() || !config.getMap(id, BldConfig.M_FRAGMENT).isEmpty()) {
334 config.setMap(id, BldConfig.M_FRAGMENT, fragments);
335 }
336
337 // contents
338 List<String> contents = new ArrayList<String>();
339 for (String pkg : bundle.getPackages()) {
340 contents.add(pkg);
341 }
342 if (!contents.isEmpty() || !config.getList(id, BldConfig.L_CONTENTS).isEmpty()) {
343 config.setList(id, BldConfig.L_CONTENTS, contents);
344 }
345
346 // dl contents
347 List<String> dlcontents = new ArrayList<String>();
348 for (String pkg : bundle.getDownloadPackages()) {
349 dlcontents.add(pkg);
350 }
351 if (!dlcontents.isEmpty() || !config.getList(id, BldConfig.L_DL_CONTENTS).isEmpty()) {
352 config.setList(id, BldConfig.L_DL_CONTENTS, dlcontents);
353 }
354
355 // libs
356 Map<String, Map<String, String>> libs = new TreeMap<String, Map<String,String>>();
357 List<String> sources = new ArrayList<String>();
358
359 // classpathEntries map to -libs or -sources
360 for (String entry : bundle.getClasspathEntrys()) {
361 // <classpathentry kind="lib" path="lib/dependee.jar"/>
362 // <classpathentry kind="src" path="src"/>
363 final String regex = ".* kind=\"([^\"]+)\" path=\"([^\"]+)\".*";
364 Pattern pattern = Pattern.compile(regex);
365 Matcher matcher = pattern.matcher(entry);
366 if (matcher.matches()) {
367 String kind = matcher.group(1);
368 String path = matcher.group(2);
369 if (kind.equals("lib")) {
370 Map<String, String> map2 = new TreeMap<String, String>();
371 map2.put(BldAttr.KIND_ATTRIBUTE, "classpath");
372 libs.put(path, map2);
373 } else if (kind.equals("src")) {
374 sources.add(path);
375 } else {
376 BldCore.error("unknown classpathentry kind=" + kind);
377 }
378 } else {
379 BldCore.error("can't match classpathEntry in: " + entry);
380 }
381 }
382
383 if (!libs.isEmpty() || !config.getMap(id, BldConfig.M_LIBS).isEmpty()) {
384 config.setMap(id, BldConfig.M_LIBS, libs);
385 }
386
387 if (!sources.isEmpty() || !config.getList(id, BldConfig.L_SRC_CONTENTS).isEmpty()) {
388 config.setList(id, BldConfig.L_SRC_CONTENTS, sources);
389 }
390
391 // composites
392 ArrayList<String> composites = new ArrayList<String>();
393 for (ISCAComposite composite : bundle.getComposites()) {
394 String path = composite.getLocation().toString();
395 // TODO relativize
396 composites.add(path);
397 }
398
399 if (!composites.isEmpty() || !config.getList(id, BldConfig.L_COMPOSITES).isEmpty()) {
400 Collections.sort(composites);
401 config.setList(id, BldConfig.L_COMPOSITES, composites);
402 }
403
404 // resources
405 ArrayList<String> resources = new ArrayList<String>();
406 for (IPath ipath : bundle.getSourcePaths()) {
407 resources.add(ipath.toString());
408 }
409
410 if (!resources.isEmpty() || !config.getList(id, BldConfig.L_RESOURCES).isEmpty()) {
411 Collections.sort(resources);
412 config.setList(id, BldConfig.L_RESOURCES, resources);
413 }
414
415 if (info.getSourceLocation() != null) {
416 BldCore.error("SourceLocation conversion not yet implemented.");
417 }
418
419 if (!info.getLibraryImports().isEmpty()) {
420 BldCore.error("LibraryImports conversion not yet implemented.");
421 }
422
423 ////////////////////
424 // simple headers
425
426 List<String> ids = config.getList(null, BldConfig.C_BUNDLES);
427 String idBsn = id != null ? id : ids.get(0);
428 String oldBsn = config.getString(id, BldConfig.S_SYM_NAME);
429 String bsn = info.getSymbolicName();
430
431 if (!bsn.equals(idBsn) || oldBsn != null)
432 config.setString(id, BldConfig.S_SYM_NAME, bsn);
433
434 String version = info.getVersion().toString();
435 if (version != null)
436 config.setString(id, BldConfig.S_VERSION, version);
437
438 String activator = info.getActivator();
439 if (activator != null)
440 config.setString(id, BldConfig.S_ACTIVATOR, activator);
441
442 Properties headers = config.getProps(null, BldConfig.P_HEADER);
443
444 setHeader(headers, id, "CATEGORY", info.getCategory());
445 setHeader(headers, id, Constants.BUNDLE_CONTACTADDRESS, info.getContactAddress());
446 setHeader(headers, id, Constants.BUNDLE_COPYRIGHT, info.getCopyright());
447 setHeader(headers, id, Constants.BUNDLE_DESCRIPTION, info.getDescription());
448 setHeader(headers, id, Constants.BUNDLE_VENDOR, info.getVendor());
449 setHeader(headers, id, Constants.BUNDLE_NAME, info.getName());
450
451 if (info.getDocURI() != null)
452 config.setProp(id, BldConfig.P_HEADER, Constants.BUNDLE_DOCURL, info.getDocURI().toString());
453
454 if (info.getLicenseURI() != null)
455 config.setProp(id, BldConfig.P_HEADER, Constants.BUNDLE_LICENSE, info.getLicenseURI().toString());
456 }
457
458 private void setHeader(Properties headers, String id, String key, String value) {
459 if (value == null)
460 value = "";
461 if (!value.equals(headers.getProperty(key, "")))
462 config.setProp(id, BldConfig.P_HEADER, key, value);
463 }
464}