blob: 0f8a332d8c82323187a4c7327f6a5e58d63bbf13 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.cauldron.bld.config;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.cauldron.bld.config.IBldProject.IBldBundle;
import org.cauldron.bld.core.BldCore;
import org.cauldron.bld.core.internal.model.eclipse.SigilBundle;
import org.cauldron.bld.core.internal.model.osgi.BundleModelElement;
import org.cauldron.sigil.model.common.VersionRange;
import org.cauldron.sigil.model.eclipse.ISCAComposite;
import org.cauldron.sigil.model.eclipse.ISigilBundle;
import org.cauldron.sigil.model.osgi.IBundleModelElement;
import org.cauldron.sigil.model.osgi.IPackageExport;
import org.cauldron.sigil.model.osgi.IPackageImport;
import org.cauldron.sigil.model.osgi.IRequiredBundle;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.osgi.framework.Version;
import aQute.lib.osgi.Constants;
public class BldConverter {
private static final String classpathFormat = "<classpathentry kind=\"%s\" path=\"%s\"/>";
private BldConfig config;
private Properties packageDefaults;
private TreeSet<String> packageWildDefaults;
public BldConverter(BldConfig config) {
this.config = config;
}
/**
* converts to an ISigilBundle.
* @param id
* @param bundle
* @return
*/
public ISigilBundle getBundle(String id, IBldBundle bundle) {
ISigilBundle sigilBundle = new SigilBundle();
IBundleModelElement info = new BundleModelElement();
sigilBundle.setBundleInfo(info);
// exports
// FIXME: UI doesn't understand export wildcard packages
for (IPackageExport export : bundle.getExports()) {
IPackageExport clone = (IPackageExport) export.clone();
clone.setParent(null);
info.addExport(clone);
}
// imports
for (IPackageImport import1 : bundle.getImports()) {
IPackageImport clone = (IPackageImport) import1.clone();
clone.setParent(null);
info.addImport(clone);
}
// requires
for (IRequiredBundle require : bundle.getRequires()) {
IRequiredBundle clone = (IRequiredBundle) require.clone();
clone.setParent(null);
info.addRequiredBundle(clone);
}
// fragment
IRequiredBundle fragment = bundle.getFragmentHost();
if (fragment != null) {
info.setFragmentHost(fragment);
}
// contents
for (String pkg : bundle.getContents()) {
sigilBundle.addPackage(pkg);
}
// downloads
for (String pkg : bundle.getDownloadContents()) {
sigilBundle.addDownloadPackage(pkg);
}
// sources
for (String source : config.getList(null, BldConfig.L_SRC_CONTENTS) ) {
sigilBundle.addClasspathEntry(String.format(classpathFormat, "src", source));
}
// libs
Map<String, Map<String, String>> libs = bundle.getLibs();
for (String path : libs.keySet()) {
Map<String, String> attr = libs.get(path);
String kind = attr.get(BldAttr.KIND_ATTRIBUTE);
String publish = attr.get(BldAttr.PUBLISH_ATTRIBUTE);
if (publish != null) {
// FIXME: UI doesn't understand publish=name
BldCore.error("Can't convert -libs publish=" + publish);
continue;
}
if ("classpath".equals(kind)) {
sigilBundle.addClasspathEntry(String.format(classpathFormat, "lib", path));
} else {
BldCore.error("Can't convert -libs kind=" + kind);
}
}
// resources
// FIXME: UI doesn't support -resources: path1=path2
Map<String, String> resources = bundle.getResources();
for (String resource : resources.keySet()) {
String fsPath = resources.get(resource);
if (!"".equals(fsPath)) {
BldCore.error("FIXME: can't convert resource: " + resource + "=" + fsPath);
}
sigilBundle.addSourcePath(new Path(resource));
}
////////////////////
// simple headers
info.setSymbolicName(bundle.getSymbolicName());
info.setVersion(Version.parseVersion(bundle.getVersion()));
String activator = bundle.getActivator();
if (activator != null)
info.setActivator(activator);
Properties headers = config.getProps(id, BldConfig.P_HEADER);
String header;
header = headers.getProperty("CATEGORY");
if (header != null)
info.setCategory(header);
header = headers.getProperty(Constants.BUNDLE_CONTACTADDRESS);
if (header != null)
info.setContactAddress(header);
header = headers.getProperty(Constants.BUNDLE_COPYRIGHT);
if (header != null)
info.setCopyright(header);
header = headers.getProperty(Constants.BUNDLE_DESCRIPTION);
if (header != null)
info.setDescription(header);
header = headers.getProperty(Constants.BUNDLE_VENDOR);
if (header != null)
info.setVendor(header);
header = headers.getProperty(Constants.BUNDLE_NAME);
if (header != null)
info.setName(header);
header = headers.getProperty(Constants.BUNDLE_DOCURL);
if (header != null)
info.setDocURI(URI.create(header));
header = headers.getProperty(Constants.BUNDLE_LICENSE);
if (header != null)
info.setDocURI(URI.create(header));
return sigilBundle;
}
private VersionRange defaultVersion(VersionRange current, String defaultRange) {
if (current.equals(VersionRange.ANY_VERSION) ||
current.equals(VersionRange.parseVersionRange(defaultRange))) {
return null;
}
return current;
}
// FIXME - copied from BldProject
private String getDefaultPackageVersion(String name) {
if (packageDefaults == null) {
packageDefaults = config.getProps(null, BldConfig.P_PACKAGE_VERSION);
packageWildDefaults = new TreeSet<String>();
for (Object key : packageDefaults.keySet()) {
String pkg = (String)key;
if (pkg.endsWith("*")) {
packageWildDefaults.add(pkg.substring(0, pkg.length() - 1));
}
}
}
String version = packageDefaults.getProperty(name);
if (version == null) {
for (String pkg : packageWildDefaults) {
if (name.startsWith(pkg)) {
version = packageDefaults.getProperty(pkg + "*");
// break; -- don't break, as we want the longest match
}
}
}
return version;
}
/**
* converts from an ISigilBundle.
*
* @param id
* @param bundle
*/
public void setBundle(String id, ISigilBundle bundle) {
IBundleModelElement info = bundle.getBundleInfo();
String bundleVersion = config.getString(id, BldConfig.S_VERSION);
// exports
Map<String, Map<String, String>> exports = new TreeMap<String, Map<String,String>>();
for (IPackageExport export : info.getExports()) {
Map<String, String> map2 = new TreeMap<String, String>();
String version = export.getVersion().toString();
if (!version.equals(bundleVersion))
map2.put(BldAttr.VERSION_ATTRIBUTE, version);
exports.put(export.getPackageName(), map2);
}
if (!exports.isEmpty() || !config.getMap(id, BldConfig.M_EXPORTS).isEmpty()) {
config.setMap(id, BldConfig.M_EXPORTS, exports);
}
// imports
Map<String, Map<String, String>> imports = new TreeMap<String, Map<String,String>>();
// FIXME: default version logic is wrong here
// if the version to be saved is the same as the default version,
// then we should _remove_ the version from the value being saved,
// since config.getMap() does not apply default versions.
for (IPackageImport import1 : info.getImports()) {
Map<String, String> map2 = new TreeMap<String, String>();
String name = import1.getPackageName();
VersionRange versions = defaultVersion(import1.getVersions(), getDefaultPackageVersion(name));
boolean isDependency = import1.isDependency();
Map<String, String> selfImport = exports.get(name);
if (selfImport != null) {
// avoid saving self-import attributes, e.g.
// org.cauldron.newton.example.fractal.engine;resolve=auto;version=1.0.0
isDependency = true;
if (versions != null) {
String exportVersion = selfImport.get(BldAttr.VERSION_ATTRIBUTE);
if (exportVersion == null)
exportVersion = bundleVersion;
if (exportVersion.equals(versions.toString())) {
versions = null;
}
}
}
if (versions != null) {
map2.put(BldAttr.VERSION_ATTRIBUTE, versions.toString());
}
if (import1.isOptional()) {
map2.put(BldAttr.RESOLUTION_ATTRIBUTE, BldAttr.RESOLUTION_OPTIONAL);
}
String resolve = BldProject.getResolve(import1, isDependency);
if (resolve != null)
map2.put(BldAttr.RESOLVE_ATTRIBUTE, resolve);
imports.put(name, map2);
}
if (!imports.isEmpty() || !config.getMap(id, BldConfig.M_IMPORTS).isEmpty()) {
config.setMap(id, BldConfig.M_IMPORTS, imports);
}
// requires
Properties defaultBundles = config.getProps(null, BldConfig.P_BUNDLE_VERSION);
Map<String, Map<String, String>> requires = new TreeMap<String, Map<String,String>>();
for (IRequiredBundle require : info.getRequiredBundles()) {
Map<String, String> map2 = new TreeMap<String, String>();
String name = require.getSymbolicName();
VersionRange versions = defaultVersion(require.getVersions(), defaultBundles.getProperty(name));
if (versions != null)
map2.put(BldAttr.VERSION_ATTRIBUTE, versions.toString());
requires.put(name, map2);
}
if (!requires.isEmpty() || !config.getMap(id, BldConfig.M_REQUIRES).isEmpty()) {
config.setMap(id, BldConfig.M_REQUIRES, requires);
}
// fragment
Map<String, Map<String, String>> fragments = new TreeMap<String, Map<String,String>>();
IRequiredBundle fragment = info.getFragmentHost();
if (fragment != null) {
Map<String, String> map2 = new TreeMap<String, String>();
String name = fragment.getSymbolicName();
VersionRange versions = defaultVersion(fragment.getVersions(), defaultBundles.getProperty(name));
if (versions != null)
map2.put(BldAttr.VERSION_ATTRIBUTE, versions.toString());
fragments.put(name, map2);
}
if (!fragments.isEmpty() || !config.getMap(id, BldConfig.M_FRAGMENT).isEmpty()) {
config.setMap(id, BldConfig.M_FRAGMENT, fragments);
}
// contents
List<String> contents = new ArrayList<String>();
for (String pkg : bundle.getPackages()) {
contents.add(pkg);
}
if (!contents.isEmpty() || !config.getList(id, BldConfig.L_CONTENTS).isEmpty()) {
config.setList(id, BldConfig.L_CONTENTS, contents);
}
// dl contents
List<String> dlcontents = new ArrayList<String>();
for (String pkg : bundle.getDownloadPackages()) {
dlcontents.add(pkg);
}
if (!dlcontents.isEmpty() || !config.getList(id, BldConfig.L_DL_CONTENTS).isEmpty()) {
config.setList(id, BldConfig.L_DL_CONTENTS, dlcontents);
}
// libs
Map<String, Map<String, String>> libs = new TreeMap<String, Map<String,String>>();
List<String> sources = new ArrayList<String>();
// classpathEntries map to -libs or -sources
for (String entry : bundle.getClasspathEntrys()) {
// <classpathentry kind="lib" path="lib/dependee.jar"/>
// <classpathentry kind="src" path="src"/>
final String regex = ".* kind=\"([^\"]+)\" path=\"([^\"]+)\".*";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(entry);
if (matcher.matches()) {
String kind = matcher.group(1);
String path = matcher.group(2);
if (kind.equals("lib")) {
Map<String, String> map2 = new TreeMap<String, String>();
map2.put(BldAttr.KIND_ATTRIBUTE, "classpath");
libs.put(path, map2);
} else if (kind.equals("src")) {
sources.add(path);
} else {
BldCore.error("unknown classpathentry kind=" + kind);
}
} else {
BldCore.error("can't match classpathEntry in: " + entry);
}
}
if (!libs.isEmpty() || !config.getMap(id, BldConfig.M_LIBS).isEmpty()) {
config.setMap(id, BldConfig.M_LIBS, libs);
}
if (!sources.isEmpty() || !config.getList(id, BldConfig.L_SRC_CONTENTS).isEmpty()) {
config.setList(id, BldConfig.L_SRC_CONTENTS, sources);
}
// composites
ArrayList<String> composites = new ArrayList<String>();
for (ISCAComposite composite : bundle.getComposites()) {
String path = composite.getLocation().toString();
// TODO relativize
composites.add(path);
}
if (!composites.isEmpty() || !config.getList(id, BldConfig.L_COMPOSITES).isEmpty()) {
Collections.sort(composites);
config.setList(id, BldConfig.L_COMPOSITES, composites);
}
// resources
ArrayList<String> resources = new ArrayList<String>();
for (IPath ipath : bundle.getSourcePaths()) {
resources.add(ipath.toString());
}
if (!resources.isEmpty() || !config.getList(id, BldConfig.L_RESOURCES).isEmpty()) {
Collections.sort(resources);
config.setList(id, BldConfig.L_RESOURCES, resources);
}
if (info.getSourceLocation() != null) {
BldCore.error("SourceLocation conversion not yet implemented.");
}
if (!info.getLibraryImports().isEmpty()) {
BldCore.error("LibraryImports conversion not yet implemented.");
}
////////////////////
// simple headers
List<String> ids = config.getList(null, BldConfig.C_BUNDLES);
String idBsn = id != null ? id : ids.get(0);
String oldBsn = config.getString(id, BldConfig.S_SYM_NAME);
String bsn = info.getSymbolicName();
if (!bsn.equals(idBsn) || oldBsn != null)
config.setString(id, BldConfig.S_SYM_NAME, bsn);
String version = info.getVersion().toString();
if (version != null)
config.setString(id, BldConfig.S_VERSION, version);
String activator = info.getActivator();
if (activator != null)
config.setString(id, BldConfig.S_ACTIVATOR, activator);
Properties headers = config.getProps(null, BldConfig.P_HEADER);
setHeader(headers, id, "CATEGORY", info.getCategory());
setHeader(headers, id, Constants.BUNDLE_CONTACTADDRESS, info.getContactAddress());
setHeader(headers, id, Constants.BUNDLE_COPYRIGHT, info.getCopyright());
setHeader(headers, id, Constants.BUNDLE_DESCRIPTION, info.getDescription());
setHeader(headers, id, Constants.BUNDLE_VENDOR, info.getVendor());
setHeader(headers, id, Constants.BUNDLE_NAME, info.getName());
if (info.getDocURI() != null)
config.setProp(id, BldConfig.P_HEADER, Constants.BUNDLE_DOCURL, info.getDocURI().toString());
if (info.getLicenseURI() != null)
config.setProp(id, BldConfig.P_HEADER, Constants.BUNDLE_LICENSE, info.getLicenseURI().toString());
}
private void setHeader(Properties headers, String id, String key, String value) {
if (value == null)
value = "";
if (!value.equals(headers.getProperty(key, "")))
config.setProp(id, BldConfig.P_HEADER, key, value);
}
}