/*
* 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.apache.felix.sigil.common.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.apache.felix.sigil.common.config.IBldProject.IBldBundle;
import org.apache.felix.sigil.common.core.BldCore;
import org.apache.felix.sigil.common.core.internal.model.eclipse.SigilBundle;
import org.apache.felix.sigil.common.core.internal.model.osgi.BundleModelElement;
import org.apache.felix.sigil.common.model.eclipse.ISigilBundle;
import org.apache.felix.sigil.common.model.osgi.IBundleModelElement;
import org.apache.felix.sigil.common.model.osgi.IPackageExport;
import org.apache.felix.sigil.common.model.osgi.IPackageImport;
import org.apache.felix.sigil.common.model.osgi.IRequiredBundle;
import org.apache.felix.sigil.common.osgi.VersionRange;
import org.apache.felix.sigil.common.osgi.VersionTable;
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);
}
// 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);
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
List<Resource> resources = bundle.getResources();
for (Resource resource : resources)
{
sigilBundle.addSourcePath(resource);
}
////////////////////
// simple headers
info.setSymbolicName(bundle.getSymbolicName());
info.setVersion(VersionTable.getVersion(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);
Map<String, Map<String, String>> exports = new TreeMap<String, Map<String, String>>();
setSimpleHeaders(id, info);
setExports(id, bundleVersion, info, exports);
// -imports and -requires are global to all bundles
setImports(null, bundleVersion, info, exports);
setRequires(null, bundleVersion, info);
setFragments(id, info);
setContents(id, info, bundle);
setLibraries(id, info, bundle);
setResources(id, info, bundle);
if (info.getSourceLocation() != null)
{
BldCore.error("SourceLocation conversion not yet implemented.");
}
if (!info.getLibraryImports().isEmpty())
{
BldCore.error("LibraryImports conversion not yet implemented.");
}
}
/**
* @param id
* @param info
*/
private void setSimpleHeaders(String id, IBundleModelElement info)
{
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 == null) throw new IllegalArgumentException("Missing bundle symbolic name");
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());
}
/**
* @param id
* @param info
* @param bundle
*/
private void setResources(String id, IBundleModelElement info, ISigilBundle bundle)
{
// resources
ArrayList<String> resources = new ArrayList<String>();
for (Resource 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);
}
}
/**
* @param id
* @param info
* @param bundle
*/
private void setLibraries(String id, IBundleModelElement info, ISigilBundle bundle)
{
// 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);
}
// -sourcedirs is global to all bundles
if (!sources.isEmpty()
|| !config.getList(null, BldConfig.L_SRC_CONTENTS).isEmpty())
{
config.setList(null, BldConfig.L_SRC_CONTENTS, sources);
}
}
/**
* @param id
* @param info
* @param bundle
*/
private void setContents(String id, IBundleModelElement info, ISigilBundle bundle)
{
// 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);
}
}
/**
* @param id
* @param info
*/
private void setFragments(String id, IBundleModelElement info)
{
Properties defaultBundles = config.getProps(null, BldConfig.P_BUNDLE_VERSION);
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);
}
}
/**
* @param id
* @param bundleVersion
* @param info
*/
private void setRequires(String id, String bundleVersion, IBundleModelElement info)
{
// 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);
}
}
/**
* @param bundleVersion
* @param info
* @param exports
*/
private void setImports(String id, String bundleVersion, IBundleModelElement info,
Map<String, Map<String, String>> 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);
}
}
/**
* @param id
* @param info
* @param bundleVersion
* @param exports
*
*/
private void setExports(String id, String bundleVersion, IBundleModelElement info,
Map<String, Map<String, String>> exports)
{
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);
}
}
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);
}
}