Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
aQute.bnd.osgi.Processor Maven / Gradle / Ivy
Go to download
The bndlib project is a general library to be used with OSGi bundles. It contains
lots of cool functionality that calculates dependencies, etc.
package aQute.bnd.osgi;
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.*;
import java.util.jar.*;
import java.util.regex.*;
import aQute.bnd.header.*;
import aQute.bnd.service.*;
import aQute.bnd.version.*;
import aQute.lib.collections.*;
import aQute.lib.io.*;
import aQute.libg.generics.*;
import aQute.service.reporter.*;
public class Processor extends Domain implements Reporter, Registry, Constants, Closeable {
static ThreadLocal current = new ThreadLocal();
static ExecutorService executor = Executors.newCachedThreadPool();
static Random random = new Random();
// TODO handle include files out of date
// TODO make splitter skip eagerly whitespace so trim is not necessary
public final static String LIST_SPLITTER = "\\s*,\\s*";
final List errors = new ArrayList();
final List warnings = new ArrayList();
final Set basicPlugins = new HashSet();
private final Set toBeClosed = new HashSet();
Set plugins;
boolean pedantic;
boolean trace;
boolean exceptions;
boolean fileMustExist = true;
private File base = new File("").getAbsoluteFile();
Properties properties;
String profile;
private Macro replacer;
private long lastModified;
private File propertiesFile;
private boolean fixup = true;
long modified;
Processor parent;
List included;
CL pluginLoader;
Collection filter;
HashSet missingCommand;
public Processor() {
properties = new Properties();
}
public Processor(Properties parent) {
properties = new Properties(parent);
}
public Processor(Processor child) {
this(child.properties);
this.parent = child;
}
public void setParent(Processor processor) {
this.parent = processor;
Properties ext = new Properties(processor.properties);
ext.putAll(this.properties);
this.properties = ext;
}
public Processor getParent() {
return parent;
}
public Processor getTop() {
if (parent == null)
return this;
return parent.getTop();
}
public void getInfo(Reporter processor, String prefix) {
if (isFailOk())
addAll(warnings, processor.getErrors(), prefix);
else
addAll(errors, processor.getErrors(), prefix);
addAll(warnings, processor.getWarnings(), prefix);
processor.getErrors().clear();
processor.getWarnings().clear();
}
public void getInfo(Reporter processor) {
getInfo(processor, "");
}
private void addAll(List to, List< ? extends T> from, String prefix) {
for (T x : from) {
to.add(prefix + x);
}
}
/**
* A processor can mark itself current for a thread.
*
* @return
*/
private Processor current() {
Processor p = current.get();
if (p == null)
return this;
return p;
}
public SetLocation warning(String string, Object... args) {
Processor p = current();
String s = formatArrays(string, args);
if (!p.warnings.contains(s))
p.warnings.add(s);
p.signal();
return location(s);
}
public SetLocation error(String string, Object... args) {
Processor p = current();
try {
if (p.isFailOk())
return p.warning(string, args);
String s = formatArrays(string, args == null ? new Object[0] : args);
if (!p.errors.contains(s))
p.errors.add(s);
return location(s);
}
finally {
p.signal();
}
}
public void progress(float progress, String format, Object... args) {
format = String.format("[%2d] %s", (int) progress, format);
trace(format, args);
}
public void progress(String format, Object... args) {
progress(-1f, format, args);
}
public SetLocation exception(Throwable t, String format, Object... args) {
return error(format, t, args);
}
public SetLocation error(String string, Throwable t, Object... args) {
Processor p = current();
try {
if (p.exceptions)
t.printStackTrace();
if (p.isFailOk()) {
return p.warning(string + ": " + t, args);
}
p.errors.add("Exception: " + t.getMessage());
String s = formatArrays(string, args == null ? new Object[0] : args);
if (!p.errors.contains(s))
p.errors.add(s);
return location(s);
}
finally {
p.signal();
}
}
public void signal() {}
public List getWarnings() {
return warnings;
}
public List getErrors() {
return errors;
}
/**
* Standard OSGi header parser.
*
* @param value
* @return
*/
static public Parameters parseHeader(String value, Processor logger) {
return new Parameters(value, logger);
}
public Parameters parseHeader(String value) {
return new Parameters(value, this);
}
public void addClose(Closeable jar) {
assert jar != null;
toBeClosed.add(jar);
}
public void removeClose(Closeable jar) {
assert jar != null;
toBeClosed.remove(jar);
}
public boolean isPedantic() {
return current().pedantic;
}
public void setPedantic(boolean pedantic) {
this.pedantic = pedantic;
}
public void use(Processor reporter) {
setPedantic(reporter.isPedantic());
setTrace(reporter.isTrace());
setBase(reporter.getBase());
setFailOk(reporter.isFailOk());
}
public static File getFile(File base, String file) {
return IO.getFile(base, file);
}
public File getFile(String file) {
return getFile(base, file);
}
/**
* Return a list of plugins that implement the given class.
*
* @param clazz
* Each returned plugin implements this class/interface
* @return A list of plugins
*/
public List getPlugins(Class clazz) {
List l = new ArrayList();
Set all = getPlugins();
for (Object plugin : all) {
if (clazz.isInstance(plugin))
l.add(clazz.cast(plugin));
}
return l;
}
/**
* Returns the first plugin it can find of the given type.
*
* @param
* @param clazz
* @return
*/
public T getPlugin(Class clazz) {
Set all = getPlugins();
for (Object plugin : all) {
if (clazz.isInstance(plugin))
return clazz.cast(plugin);
}
return null;
}
/**
* Return a list of plugins. Plugins are defined with the -plugin command.
* They are class names, optionally associated with attributes. Plugins can
* implement the Plugin interface to see these attributes. Any object can be
* a plugin.
*
* @return
*/
protected synchronized Set getPlugins() {
if (this.plugins != null)
return this.plugins;
missingCommand = new HashSet();
Set list = new LinkedHashSet();
// The owner of the plugin is always in there.
list.add(this);
setTypeSpecificPlugins(list);
if (parent != null)
list.addAll(parent.getPlugins());
// We only use plugins now when they are defined on our level
// and not if it is in our parent. We inherit from our parent
// through the previous block.
if (properties.containsKey(PLUGIN)) {
String spe = getProperty(PLUGIN);
if (spe.equals(NONE))
return new LinkedHashSet();
String pluginPath = getProperty(PLUGINPATH);
loadPlugins(list, spe, pluginPath);
}
return this.plugins = list;
}
/**
* @param list
* @param spe
*/
protected void loadPlugins(Set list, String spe, String pluginPath) {
Parameters plugins = new Parameters(spe);
CL loader = getLoader();
// First add the plugin-specific paths from their path: directives
for (Entry entry : plugins.entrySet()) {
String key = removeDuplicateMarker(entry.getKey());
String path = entry.getValue().get(PATH_DIRECTIVE);
if (path != null) {
String parts[] = path.split("\\s*,\\s*");
try {
for (String p : parts) {
File f = getFile(p).getAbsoluteFile();
loader.add(f.toURI().toURL());
}
}
catch (Exception e) {
error("Problem adding path %s to loader for plugin %s. Exception: (%s)", path, key, e);
}
}
}
// Next add -pluginpath entries
if (pluginPath != null && pluginPath.length() > 0) {
StringTokenizer tokenizer = new StringTokenizer(pluginPath, ",");
while (tokenizer.hasMoreTokens()) {
String path = tokenizer.nextToken().trim();
try {
File f = getFile(path).getAbsoluteFile();
loader.add(f.toURI().toURL());
}
catch (Exception e) {
error("Problem adding path %s from global plugin path. Exception: %s", path, e);
}
}
}
// Load the plugins
for (Entry entry : plugins.entrySet()) {
String key = entry.getKey();
try {
trace("Using plugin %s", key);
// Plugins could use the same class with different
// parameters so we could have duplicate names Remove
// the ! added by the parser to make each name unique.
key = removeDuplicateMarker(key);
try {
Class< ? > c = loader.loadClass(key);
Object plugin = c.newInstance();
customize(plugin, entry.getValue());
if (plugin instanceof Closeable) {
addClose((Closeable) plugin);
}
list.add(plugin);
}
catch (Throwable t) {
// We can defer the error if the plugin specifies
// a command name. In that case, we'll verify that
// a bnd file does not contain any references to a
// plugin
// command. The reason this feature was added was
// to compile plugin classes with the same build.
String commands = entry.getValue().get(COMMAND_DIRECTIVE);
if (commands == null)
error("Problem loading the plugin: %s exception: (%s)", key, t);
else {
Collection cs = split(commands);
missingCommand.addAll(cs);
}
}
}
catch (Throwable e) {
error("Problem loading the plugin: %s exception: (%s)", key, e);
}
}
}
protected void setTypeSpecificPlugins(Set list) {
list.add(executor);
list.add(random);
list.addAll(basicPlugins);
}
/**
* @param plugin
* @param entry
*/
protected T customize(T plugin, Attrs map) {
if (plugin instanceof Plugin) {
if (map != null)
((Plugin) plugin).setProperties(map);
((Plugin) plugin).setReporter(this);
}
if (plugin instanceof RegistryPlugin) {
((RegistryPlugin) plugin).setRegistry(this);
}
return plugin;
}
@Override
public boolean isFailOk() {
String v = getProperty(Analyzer.FAIL_OK, null);
return v != null && v.equalsIgnoreCase("true");
}
public File getBase() {
return base;
}
public void setBase(File base) {
this.base = base;
}
public void clear() {
errors.clear();
warnings.clear();
}
public void trace(String msg, Object... parms) {
Processor p = current();
if (p.trace) {
System.err.printf("# " + msg + "%n", parms);
}
}
public List newList() {
return new ArrayList();
}
public Set newSet() {
return new TreeSet();
}
public static Map newMap() {
return new LinkedHashMap();
}
public static Map newHashMap() {
return new LinkedHashMap();
}
public List newList(Collection t) {
return new ArrayList(t);
}
public Set newSet(Collection t) {
return new TreeSet(t);
}
public Map newMap(Map t) {
return new LinkedHashMap(t);
}
public void close() {
for (Closeable c : toBeClosed) {
try {
c.close();
}
catch (IOException e) {
// Who cares?
}
}
toBeClosed.clear();
}
public String _basedir(@SuppressWarnings("unused")
String args[]) {
if (base == null)
throw new IllegalArgumentException("No base dir set");
return base.getAbsolutePath();
}
/**
* Property handling ...
*
* @return
*/
public Properties getProperties() {
if (fixup) {
fixup = false;
begin();
}
return properties;
}
public String getProperty(String key) {
return getProperty(key, null);
}
public void mergeProperties(File file, boolean override) {
if (file.isFile()) {
try {
Properties properties = loadProperties(file);
mergeProperties(properties, override);
}
catch (Exception e) {
error("Error loading properties file: " + file);
}
} else {
if (!file.exists())
error("Properties file does not exist: " + file);
else
error("Properties file must a file, not a directory: " + file);
}
}
public void mergeProperties(Properties properties, boolean override) {
for (Enumeration< ? > e = properties.propertyNames(); e.hasMoreElements();) {
String key = (String) e.nextElement();
String value = properties.getProperty(key);
if (override || !getProperties().containsKey(key))
setProperty(key, value);
}
}
public void setProperties(Properties properties) {
doIncludes(getBase(), properties);
this.properties.putAll(properties);
}
public void addProperties(File file) throws Exception {
addIncluded(file);
Properties p = loadProperties(file);
setProperties(p);
}
public void addProperties(Map< ? , ? > properties) {
for (Entry< ? , ? > entry : properties.entrySet()) {
setProperty(entry.getKey().toString(), entry.getValue() + "");
}
}
public synchronized void addIncluded(File file) {
if (included == null)
included = new ArrayList();
included.add(file);
}
/**
* Inspect the properties and if you find -includes parse the line included
* manifest files or properties files. The files are relative from the given
* base, this is normally the base for the analyzer.
*
* @param ubase
* @param p
* @param done
* @throws IOException
* @throws IOException
*/
private void doIncludes(File ubase, Properties p) {
String includes = p.getProperty(INCLUDE);
if (includes != null) {
includes = getReplacer().process(includes);
p.remove(INCLUDE);
Collection clauses = new Parameters(includes).keySet();
for (String value : clauses) {
boolean fileMustExist = true;
boolean overwrite = true;
while (true) {
if (value.startsWith("-")) {
fileMustExist = false;
value = value.substring(1).trim();
} else if (value.startsWith("~")) {
// Overwrite properties!
overwrite = false;
value = value.substring(1).trim();
} else
break;
}
try {
File file = getFile(ubase, value).getAbsoluteFile();
if (!file.isFile() && fileMustExist) {
error("Included file " + file + (file.exists() ? " does not exist" : " is directory"));
} else
doIncludeFile(file, overwrite, p);
}
catch (Exception e) {
if (fileMustExist)
error("Error in processing included file: " + value, e);
}
}
}
}
/**
* @param file
* @param parent
* @param done
* @param overwrite
* @throws FileNotFoundException
* @throws IOException
*/
public void doIncludeFile(File file, boolean overwrite, Properties target) throws Exception {
doIncludeFile(file, overwrite, target, null);
}
/**
* @param file
* @param parent
* @param done
* @param overwrite
* @param extensionName
* @throws FileNotFoundException
* @throws IOException
*/
public void doIncludeFile(File file, boolean overwrite, Properties target, String extensionName) throws Exception {
if (included != null && included.contains(file)) {
error("Cyclic or multiple include of " + file);
} else {
addIncluded(file);
updateModified(file.lastModified(), file.toString());
InputStream in = new FileInputStream(file);
try {
Properties sub;
if (file.getName().toLowerCase().endsWith(".mf")) {
sub = getManifestAsProperties(in);
} else
sub = loadProperties(in, file.getAbsolutePath());
doIncludes(file.getParentFile(), sub);
// make sure we do not override properties
for (Map.Entry< ? , ? > entry : sub.entrySet()) {
String key = (String) entry.getKey();
String value = (String) entry.getValue();
if (overwrite || !target.containsKey(key)) {
target.setProperty(key, value);
} else if (extensionName != null) {
String extensionKey = extensionName + "." + key;
if (!target.containsKey(extensionKey))
target.setProperty(extensionKey, value);
}
}
}
finally {
IO.close(in);
}
}
}
public void unsetProperty(String string) {
getProperties().remove(string);
}
public boolean refresh() {
plugins = null; // We always refresh our plugins
if (propertiesFile == null)
return false;
boolean changed = updateModified(propertiesFile.lastModified(), "properties file");
if (included != null) {
for (File file : included) {
if (changed)
break;
changed |= !file.exists() || updateModified(file.lastModified(), "include file: " + file);
}
}
profile = getProperty(PROFILE); // Used in property access
if (changed) {
forceRefresh();
return true;
}
return false;
}
/**
*
*/
public void forceRefresh() {
included = null;
properties.clear();
setProperties(propertiesFile, base);
propertiesChanged();
}
public void propertiesChanged() {}
/**
* Set the properties by file. Setting the properties this way will also set
* the base for this analyzer. After reading the properties, this will call
* setProperties(Properties) which will handle the includes.
*
* @param propertiesFile
* @throws FileNotFoundException
* @throws IOException
*/
public void setProperties(File propertiesFile) throws IOException {
propertiesFile = propertiesFile.getAbsoluteFile();
setProperties(propertiesFile, propertiesFile.getParentFile());
}
public void setProperties(File propertiesFile, File base) {
this.propertiesFile = propertiesFile.getAbsoluteFile();
setBase(base);
try {
if (propertiesFile.isFile()) {
// System.err.println("Loading properties " + propertiesFile);
long modified = propertiesFile.lastModified();
if (modified > System.currentTimeMillis() + 100) {
System.err.println("Huh? This is in the future " + propertiesFile);
this.modified = System.currentTimeMillis();
} else
this.modified = modified;
included = null;
Properties p = loadProperties(propertiesFile);
setProperties(p);
} else {
if (fileMustExist) {
error("No such properties file: " + propertiesFile);
}
}
}
catch (IOException e) {
error("Could not load properties " + propertiesFile);
}
}
protected void begin() {
if (isTrue(getProperty(PEDANTIC)))
setPedantic(true);
}
public static boolean isTrue(String value) {
if (value == null)
return false;
return !"false".equalsIgnoreCase(value);
}
/**
* Get a property without preprocessing it with a proper default
*
* @param headerName
* @param deflt
* @return
*/
public String getUnprocessedProperty(String key, String deflt) {
return getProperties().getProperty(key, deflt);
}
/**
* Get a property with preprocessing it with a proper default
*
* @param headerName
* @param deflt
* @return
*/
public String getProperty(String key, String deflt) {
String value = null;
Instruction ins = new Instruction(key);
if (!ins.isLiteral()) {
// Handle a wildcard key, make sure they're sorted
// for consistency
SortedList sortedList = SortedList.fromIterator(iterator());
StringBuilder sb = new StringBuilder();
String del = "";
for (String k : sortedList) {
if (ins.matches(k)) {
String v = getProperty(k, null);
if (v != null) {
sb.append(del);
del = ",";
sb.append(v);
}
}
}
if (sb.length() == 0)
return deflt;
return sb.toString();
}
Processor source = this;
// Use the key as is first, if found ok
if (filter != null && filter.contains(key)) {
value = (String) getProperties().get(key);
} else {
while (source != null) {
value = (String) source.getProperties().get(key);
if (value != null)
break;
source = source.getParent();
}
}
// Check if we found a value, if not, try to prefix
// it with a profile if found and search again. profiles
// are a simple name that is prefixed like [profile]. This
// allows different variables to be used in different profiles.
if (value == null && profile != null) {
String pkey = "[" + profile + "]" + key;
if (filter != null && filter.contains(key)) {
value = (String) getProperties().get(pkey);
} else {
while (source != null) {
value = (String) source.getProperties().get(pkey);
if (value != null)
break;
source = source.getParent();
}
}
}
if (value != null)
return getReplacer().process(value, source);
else if (deflt != null)
return getReplacer().process(deflt, this);
else
return null;
}
/**
* Helper to load a properties file from disk.
*
* @param file
* @return
* @throws IOException
*/
public Properties loadProperties(File file) throws IOException {
updateModified(file.lastModified(), "Properties file: " + file);
InputStream in = new FileInputStream(file);
try {
Properties p = loadProperties(in, file.getAbsolutePath());
return p;
}
finally {
in.close();
}
}
Properties loadProperties(InputStream in, String name) throws IOException {
int n = name.lastIndexOf('/');
if (n > 0)
name = name.substring(0, n);
if (name.length() == 0)
name = ".";
try {
Properties p = new Properties();
p.load(in);
return replaceAll(p, "\\$\\{\\.\\}", name);
}
catch (Exception e) {
error("Error during loading properties file: " + name + ", error:" + e);
return new Properties();
}
}
/**
* Replace a string in all the values of the map. This can be used to
* preassign variables that change. I.e. the base directory ${.} for a
* loaded properties
*/
public static Properties replaceAll(Properties p, String pattern, String replacement) {
Properties result = new Properties();
for (Iterator> i = p.entrySet().iterator(); i.hasNext();) {
Map.Entry entry = i.next();
String key = (String) entry.getKey();
String value = (String) entry.getValue();
value = value.replaceAll(pattern, replacement);
result.put(key, value);
}
return result;
}
/**
* Print a standard Map based OSGi header.
*
* @param exports
* map { name => Map { attribute|directive => value } }
* @return the clauses
* @throws IOException
*/
public static String printClauses(Map< ? , ? extends Map< ? , ? >> exports) throws IOException {
return printClauses(exports, false);
}
public static String printClauses(Map< ? , ? extends Map< ? , ? >> exports, @SuppressWarnings("unused")
boolean checkMultipleVersions) throws IOException {
StringBuilder sb = new StringBuilder();
String del = "";
for (Entry< ? , ? extends Map< ? , ? >> entry : exports.entrySet()) {
String name = entry.getKey().toString();
Map< ? , ? > clause = entry.getValue();
// We allow names to be duplicated in the input
// by ending them with '~'. This is necessary to use
// the package names as keys. However, we remove these
// suffixes in the output so that you can set multiple
// exports with different attributes.
String outname = removeDuplicateMarker(name);
sb.append(del);
sb.append(outname);
printClause(clause, sb);
del = ",";
}
return sb.toString();
}
public static void printClause(Map< ? , ? > map, StringBuilder sb) throws IOException {
for (Entry< ? , ? > entry : map.entrySet()) {
Object key = entry.getKey();
// Skip directives we do not recognize
if (key.equals(NO_IMPORT_DIRECTIVE) || key.equals(PROVIDE_DIRECTIVE) || key.equals(SPLIT_PACKAGE_DIRECTIVE)
|| key.equals(FROM_DIRECTIVE))
continue;
String value = ((String) entry.getValue()).trim();
sb.append(";");
sb.append(key);
sb.append("=");
quote(sb, value);
}
}
/**
* @param sb
* @param value
* @return
* @throws IOException
*/
public static boolean quote(Appendable sb, String value) throws IOException {
boolean clean = (value.length() >= 2 && value.charAt(0) == '"' && value.charAt(value.length() - 1) == '"')
|| Verifier.TOKEN.matcher(value).matches();
if (!clean)
sb.append("\"");
sb.append(value);
if (!clean)
sb.append("\"");
return clean;
}
public Macro getReplacer() {
if (replacer == null)
return replacer = new Macro(this, getMacroDomains());
return replacer;
}
/**
* This should be overridden by subclasses to add extra macro command
* domains on the search list.
*
* @return
*/
protected Object[] getMacroDomains() {
return new Object[] {};
}
/**
* Return the properties but expand all macros. This always returns a new
* Properties object that can be used in any way.
*
* @return
*/
public Properties getFlattenedProperties() {
return getReplacer().getFlattenedProperties();
}
/**
* Return all inherited property keys
*
* @return
*/
public Set getPropertyKeys(boolean inherit) {
Set result;
if (parent == null || !inherit) {
result = Create.set();
} else
result = parent.getPropertyKeys(inherit);
for (Object o : properties.keySet())
result.add(o.toString());
return result;
}
public boolean updateModified(long time, @SuppressWarnings("unused")
String reason) {
if (time > lastModified) {
lastModified = time;
return true;
}
return false;
}
public long lastModified() {
return lastModified;
}
/**
* Add or override a new property.
*
* @param key
* @param value
*/
public void setProperty(String key, String value) {
checkheader: for (int i = 0; i < headers.length; i++) {
if (headers[i].equalsIgnoreCase(value)) {
value = headers[i];
break checkheader;
}
}
getProperties().put(key, value);
}
/**
* Read a manifest but return a properties object.
*
* @param in
* @return
* @throws IOException
*/
public static Properties getManifestAsProperties(InputStream in) throws IOException {
Properties p = new Properties();
Manifest manifest = new Manifest(in);
for (Iterator it = manifest.getMainAttributes().keySet().iterator(); it.hasNext();) {
Attributes.Name key = (Attributes.Name) it.next();
String value = manifest.getMainAttributes().getValue(key);
p.put(key.toString(), value);
}
return p;
}
public File getPropertiesFile() {
return propertiesFile;
}
public void setFileMustExist(boolean mustexist) {
fileMustExist = mustexist;
}
static public String read(InputStream in) throws Exception {
InputStreamReader ir = new InputStreamReader(in, "UTF8");
StringBuilder sb = new StringBuilder();
try {
char chars[] = new char[1000];
int size = ir.read(chars);
while (size > 0) {
sb.append(chars, 0, size);
size = ir.read(chars);
}
}
finally {
ir.close();
}
return sb.toString();
}
/**
* Join a list.
*
* @param args
* @return
*/
public static String join(Collection< ? > list, String delimeter) {
return join(delimeter, list);
}
public static String join(String delimeter, Collection< ? >... list) {
StringBuilder sb = new StringBuilder();
String del = "";
if (list != null) {
for (Collection< ? > l : list) {
for (Object item : l) {
sb.append(del);
sb.append(item);
del = delimeter;
}
}
}
return sb.toString();
}
public static String join(Object[] list, String delimeter) {
if (list == null)
return "";
StringBuilder sb = new StringBuilder();
String del = "";
for (Object item : list) {
sb.append(del);
sb.append(item);
del = delimeter;
}
return sb.toString();
}
public static String join(Collection< ? >... list) {
return join(",", list);
}
public static String join(T list[]) {
return join(list, ",");
}
public static void split(String s, Collection set) {
String elements[] = s.trim().split(LIST_SPLITTER);
for (String element : elements) {
if (element.length() > 0)
set.add(element);
}
}
public static Collection split(String s) {
return split(s, LIST_SPLITTER);
}
public static Collection split(String s, String splitter) {
if (s != null)
s = s.trim();
if (s == null || s.trim().length() == 0)
return Collections.emptyList();
return Arrays.asList(s.split(splitter));
}
public static String merge(String... strings) {
ArrayList result = new ArrayList();
for (String s : strings) {
if (s != null)
split(s, result);
}
return join(result);
}
public boolean isExceptions() {
return exceptions;
}
public void setExceptions(boolean exceptions) {
this.exceptions = exceptions;
}
/**
* Make the file short if it is inside our base directory, otherwise long.
*
* @param f
* @return
*/
public String normalize(String f) {
if (f.startsWith(base.getAbsolutePath() + "/"))
return f.substring(base.getAbsolutePath().length() + 1);
return f;
}
public String normalize(File f) {
return normalize(f.getAbsolutePath());
}
public static String removeDuplicateMarker(String key) {
int i = key.length() - 1;
while (i >= 0 && key.charAt(i) == DUPLICATE_MARKER)
--i;
return key.substring(0, i + 1);
}
public static boolean isDuplicate(String name) {
return name.length() > 0 && name.charAt(name.length() - 1) == DUPLICATE_MARKER;
}
public void setTrace(boolean x) {
trace = x;
}
static class CL extends URLClassLoader {
CL() {
super(new URL[0], Processor.class.getClassLoader());
}
void add(URL url) {
URL urls[] = getURLs();
for (URL u : urls) {
if (u.equals(url))
return;
}
super.addURL(url);
}
@Override
public Class< ? > loadClass(String name) throws NoClassDefFoundError {
try {
Class< ? > c = super.loadClass(name);
return c;
}
catch (Throwable t) {
StringBuilder sb = new StringBuilder();
sb.append(name);
sb.append(" not found, parent: ");
sb.append(getParent());
sb.append(" urls:");
sb.append(Arrays.toString(getURLs()));
sb.append(" exception:");
sb.append(t);
throw new NoClassDefFoundError(sb.toString());
}
}
}
private CL getLoader() {
if (pluginLoader == null) {
pluginLoader = new CL();
}
return pluginLoader;
}
/*
* Check if this is a valid project.
*/
public boolean exists() {
return base != null && base.isDirectory() && propertiesFile != null && propertiesFile.isFile();
}
public boolean isOk() {
return isFailOk() || (getErrors().size() == 0);
}
public boolean check(String... pattern) throws IOException {
Set missed = Create.set();
if (pattern != null) {
for (String p : pattern) {
boolean match = false;
Pattern pat = Pattern.compile(p);
for (Iterator i = errors.iterator(); i.hasNext();) {
if (pat.matcher(i.next()).find()) {
i.remove();
match = true;
}
}
for (Iterator i = warnings.iterator(); i.hasNext();) {
if (pat.matcher(i.next()).find()) {
i.remove();
match = true;
}
}
if (!match)
missed.add(p);
}
}
if (missed.isEmpty() && isPerfect())
return true;
if (!missed.isEmpty())
System.err.println("Missed the following patterns in the warnings or errors: " + missed);
report(System.err);
return false;
}
protected void report(Appendable out) throws IOException {
if (errors.size() > 0) {
out.append(String.format("-----------------%nErrors%n"));
for (int i = 0; i < errors.size(); i++) {
out.append(String.format("%03d: %s%n", i, errors.get(i)));
}
}
if (warnings.size() > 0) {
out.append(String.format("-----------------%nWarnings%n"));
for (int i = 0; i < warnings.size(); i++) {
out.append(String.format("%03d: %s%n", i, warnings.get(i)));
}
}
}
public boolean isPerfect() {
return getErrors().size() == 0 && getWarnings().size() == 0;
}
public void setForceLocal(Collection local) {
filter = local;
}
/**
* Answer if the name is a missing plugin's command name. If a bnd file
* contains the command name of a plugin, and that plugin is not available,
* then an error is reported during manifest calculation. This allows the
* plugin to fail to load when it is not needed. We first get the plugins to
* ensure it is properly initialized.
*
* @param name
* @return
*/
public boolean isMissingPlugin(String name) {
getPlugins();
return missingCommand != null && missingCommand.contains(name);
}
/**
* Append two strings to for a path in a ZIP or JAR file. It is guaranteed
* to return a string that does not start, nor ends with a '/', while it is
* properly separated with slashes. Double slashes are properly removed.
*
*
* "/" + "abc/def/" becomes "abc/def"
*
* @param prefix
* @param suffix
* @return
*/
public static String appendPath(String... parts) {
StringBuilder sb = new StringBuilder();
boolean lastSlash = true;
for (String part : parts) {
for (int i = 0; i < part.length(); i++) {
char c = part.charAt(i);
if (c == '/') {
if (!lastSlash)
sb.append('/');
lastSlash = true;
} else {
sb.append(c);
lastSlash = false;
}
}
if (!lastSlash && sb.length() > 0) {
sb.append('/');
lastSlash = true;
}
}
if (lastSlash && sb.length() > 0)
sb.deleteCharAt(sb.length() - 1);
return sb.toString();
}
/**
* Parse the a=b strings and return a map of them.
*
* @param attrs
* @param clazz
* @return
*/
public static Attrs doAttrbutes(Object[] attrs, Clazz clazz, Macro macro) {
Attrs map = new Attrs();
if (attrs == null || attrs.length == 0)
return map;
for (Object a : attrs) {
String attr = (String) a;
int n = attr.indexOf("=");
if (n > 0) {
map.put(attr.substring(0, n), macro.process(attr.substring(n + 1)));
} else
throw new IllegalArgumentException(formatArrays(
"Invalid attribute on package-info.java in %s , %s. Must be = ", clazz, attr));
}
return map;
}
/**
* This method is the same as String.format but it makes sure that any
* arrays are transformed to strings.
*
* @param string
* @param parms
* @return
*/
public static String formatArrays(String string, Object... parms) {
Object[] parms2 = parms;
Object[] output = new Object[parms.length];
for (int i = 0; i < parms.length; i++) {
output[i] = makePrintable(parms[i]);
}
return String.format(string, parms2);
}
/**
* Check if the object is an array and turn it into a string if it is,
* otherwise unchanged.
*
* @param object
* the object to make printable
* @return a string if it was an array or the original object
*/
public static Object makePrintable(Object object) {
if (object == null)
return object;
if (object.getClass().isArray()) {
Object[] array = (Object[]) object;
Object[] output = new Object[array.length];
for (int i = 0; i < array.length; i++) {
output[i] = makePrintable(array[i]);
}
return Arrays.toString(output);
}
return object;
}
public static String append(String... strings) {
List result = Create.list();
for (String s : strings) {
result.addAll(split(s));
}
return join(result);
}
public synchronized Class< ? > getClass(String type, File jar) throws Exception {
CL cl = getLoader();
cl.add(jar.toURI().toURL());
return cl.loadClass(type);
}
public boolean isTrace() {
return current().trace;
}
public static long getDuration(String tm, long dflt) {
if (tm == null)
return dflt;
tm = tm.toUpperCase();
TimeUnit unit = TimeUnit.MILLISECONDS;
Matcher m = Pattern
.compile("\\s*(\\d+)\\s*(NANOSECONDS|MICROSECONDS|MILLISECONDS|SECONDS|MINUTES|HOURS|DAYS)?").matcher(
tm);
if (m.matches()) {
long duration = Long.parseLong(tm);
String u = m.group(2);
if (u != null)
unit = TimeUnit.valueOf(u);
duration = TimeUnit.MILLISECONDS.convert(duration, unit);
return duration;
}
return dflt;
}
/**
* Generate a random string, which is guaranteed to be a valid Java
* identifier (first character is an ASCII letter, subsequent characters are
* ASCII letters or numbers). Takes an optional parameter for the length of
* string to generate; default is 8 characters.
*/
public String _random(String[] args) {
int numchars = 8;
if (args.length > 1) {
try {
numchars = Integer.parseInt(args[1]);
}
catch (NumberFormatException e) {
throw new IllegalArgumentException("Invalid character count parameter in ${random} macro.");
}
}
synchronized (Processor.class) {
if (random == null)
random = new Random();
}
char[] letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
char[] alphanums = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray();
char[] array = new char[numchars];
for (int i = 0; i < numchars; i++) {
char c;
if (i == 0)
c = letters[random.nextInt(letters.length)];
else
c = alphanums[random.nextInt(alphanums.length)];
array[i] = c;
}
return new String(array);
}
/**
*
* Generates a Capability string, in the format specified by the OSGi
* Provide-Capability header, representing the current native platform
* according to OSGi RFC 188. For example on Windows7 running on an x86_64
* processor it should generate the following:
*
*
*
* osgi.native;osgi.native.osname:List<String>="Windows7,Windows 7,Win32";osgi.native.osversion:Version=6.1.0;osgi.native.processor:List<String>="x86-64,amd64,em64t,x86_64"
*
*
* @param args
* Ignored; reserved for future use.
*/
public String _native_capability(String[] args) {
StringBuilder builder = new StringBuilder().append("osgi.native");
try {
// Operating System name and version
String osnames;
Version osversion;
String sysPropOsName = System.getProperty("os.name");
String sysPropOsVersion = System.getProperty("os.version");
if (sysPropOsName.startsWith("Windows")) {
if (sysPropOsVersion.startsWith("6.2")) {
osversion = new Version(6,2,0);
osnames = "Windows8,Windows 8,Win32";
} else if (sysPropOsVersion.startsWith("6.1")) {
osversion = new Version(6, 1, 0);
osnames = "Windows7,Windows 7,Win32";
} else if (sysPropOsName.startsWith("6.0")) {
osversion = new Version(6, 0, 0);
osnames = "WindowsVista,WinVista,Windows Vista,Win32";
} else if (sysPropOsName.startsWith("5.1")) {
osversion = new Version(5, 1, 0);
osnames = "WindowsXP,WinXP,Windows XP,Win32";
} else {
throw new IllegalArgumentException(String.format("Unrecognised or unsupported Windows version while processing ${native} macro: %s version %s. Supported: XP, Vista, Win7, Win8.", sysPropOsName, sysPropOsVersion));
}
} else if (sysPropOsName.startsWith("Mac OS X")) {
osnames = "MacOSX,Mac OS X";
osversion = new Version(sysPropOsVersion);
} else if (sysPropOsName.toLowerCase().startsWith("linux")) {
osnames = "Linux";
osversion = new Version(sysPropOsVersion);
} else if (sysPropOsName.startsWith("Solaris")) {
osnames = "Solaris";
osversion = new Version(sysPropOsVersion);
} else if (sysPropOsName.startsWith("AIX")) {
osnames = "AIX";
osversion = new Version(sysPropOsVersion);
} else if (sysPropOsName.startsWith("HP-UX")) {
osnames = "HPUX,hp-ux";
osversion = new Version(sysPropOsVersion);
} else {
throw new IllegalArgumentException(String.format("Unrecognised or unsupported OS while processing ${native} macro: %s version %s. Supported: Windows, Mac OS X, Linux, Solaris, AIX, HP-UX.", sysPropOsName, sysPropOsVersion));
}
builder.append(";osgi.native.osname:List=\"").append(osnames).append('"');
builder.append(";osgi.native.osversion:Version=").append(osversion.toString());
// Processor
String processorNames;
String arch = System.getProperty("os.arch");
if ("x86_64".equals(arch))
processorNames = "x86-64,amd64,em64t,x86_64";
else if ("x86".equals(arch))
processorNames = "x86,pentium,i386,i486,i586,i686";
else
throw new IllegalArgumentException(String.format("Unrecognised/unsupported processor name '%s' in ${native} macro.", arch));
builder.append(";osgi.native.processor:List=\"").append(processorNames).append('"');
} catch (SecurityException e) {
throw new IllegalArgumentException("Security error retrieving system properties while processing ${native} macro.");
}
return builder.toString();
}
/**
* Set the current command thread. This must be balanced with the
* {@link #end(Processor)} method. The method returns the previous command
* owner or null. The command owner will receive all warnings and error
* reports.
*/
protected Processor beginHandleErrors(String message) {
trace("begin %s", message);
Processor previous = current.get();
current.set(this);
return previous;
}
/**
* End a command. Will restore the previous command owner.
*
* @param previous
*/
protected void endHandleErrors(Processor previous) {
trace("end");
current.set(previous);
}
public static Executor getExecutor() {
return executor;
}
/**
* These plugins are added to the total list of plugins. The separation is
* necessary because the list of plugins is refreshed now and then so we
* need to be able to add them at any moment in time.
*
* @param plugin
*/
public synchronized void addBasicPlugin(Object plugin) {
basicPlugins.add(plugin);
if (plugins != null)
plugins.add(plugin);
}
public synchronized void removeBasicPlugin(Object plugin) {
basicPlugins.remove(plugin);
if (plugins != null)
plugins.remove(plugin);
}
public List getIncluded() {
return included;
}
/**
* Overrides for the Domain class
*/
@Override
public String get(String key) {
return getProperty(key);
}
@Override
public String get(String key, String deflt) {
return getProperty(key, deflt);
}
@Override
public void set(String key, String value) {
getProperties().setProperty(key, value);
}
@Override
public Iterator iterator() {
Set keys = keySet();
final Iterator it = keys.iterator();
return new Iterator() {
String current;
public boolean hasNext() {
return it.hasNext();
}
public String next() {
return current = it.next().toString();
}
public void remove() {
getProperties().remove(current);
}
};
}
public Set keySet() {
Set set;
if (parent == null)
set = Create.set();
else
set = parent.keySet();
for (Object o : properties.keySet())
set.add(o.toString());
return set;
}
/**
* Printout of the status of this processor for toString()
*/
@Override
public String toString() {
try {
StringBuilder sb = new StringBuilder();
report(sb);
return sb.toString();
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Utiltity to replace an extension
*
* @param s
* @param extension
* @param newExtension
* @return
*/
public String replaceExtension(String s, String extension, String newExtension) {
if (s.endsWith(extension))
s = s.substring(0, s.length() - extension.length());
return s + newExtension;
}
/**
* Create a location object and add it to the locations
*
* @param s
* @return
*/
List locations = new ArrayList();
static class SetLocationImpl extends Location implements SetLocation {
public SetLocationImpl(String s) {
this.message = s;
}
public SetLocation file(String file) {
this.file = file;
return this;
}
public SetLocation header(String header) {
this.header = header;
return this;
}
public SetLocation context(String context) {
this.context = context;
return this;
}
public SetLocation method(String methodName) {
this.methodName = methodName;
return this;
}
public SetLocation line(int n) {
this.line = n;
return this;
}
public SetLocation reference(String reference) {
this.reference = reference;
return this;
}
}
private SetLocation location(String s) {
SetLocationImpl loc = new SetLocationImpl(s);
locations.add(loc);
return loc;
}
public Location getLocation(String msg) {
for (Location l : locations)
if ((l.message != null) && l.message.equals(msg))
return l;
return null;
}
}