/biz.aQute.bndlib/src/aQute/bnd/osgi/Processor.java
Java | 1780 lines | 1226 code | 243 blank | 311 comment | 274 complexity | 4234efc78b77de16b6189de56bda79f7 MD5 | raw file
Possible License(s): Apache-2.0
- 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<Processor> current = new ThreadLocal<Processor>();
- 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<String> errors = new ArrayList<String>();
- final List<String> warnings = new ArrayList<String>();
- final Set<Object> basicPlugins = new HashSet<Object>();
- private final Set<Closeable> toBeClosed = new HashSet<Closeable>();
- Set<Object> 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<File> included;
- CL pluginLoader;
- Collection<String> filter;
- HashSet<String> 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 <T> void addAll(List<String> 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<String> getWarnings() {
- return warnings;
- }
- public List<String> 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 <T> List<T> getPlugins(Class<T> clazz) {
- List<T> l = new ArrayList<T>();
- Set<Object> 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 <T>
- * @param clazz
- * @return
- */
- public <T> T getPlugin(Class<T> clazz) {
- Set<Object> 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<Object> getPlugins() {
- if (this.plugins != null)
- return this.plugins;
- missingCommand = new HashSet<String>();
- Set<Object> list = new LinkedHashSet<Object>();
- // 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<Object>();
- String pluginPath = getProperty(PLUGINPATH);
- loadPlugins(list, spe, pluginPath);
- }
- return this.plugins = list;
- }
- /**
- * @param list
- * @param spe
- */
- protected void loadPlugins(Set<Object> 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<String,Attrs> 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<String,Attrs> 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<String> cs = split(commands);
- missingCommand.addAll(cs);
- }
- }
- }
- catch (Throwable e) {
- error("Problem loading the plugin: %s exception: (%s)", key, e);
- }
- }
- }
- protected void setTypeSpecificPlugins(Set<Object> list) {
- list.add(executor);
- list.add(random);
- list.addAll(basicPlugins);
- }
- /**
- * @param plugin
- * @param entry
- */
- protected <T> 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 <T> List<T> newList() {
- return new ArrayList<T>();
- }
- public <T> Set<T> newSet() {
- return new TreeSet<T>();
- }
- public static <K, V> Map<K,V> newMap() {
- return new LinkedHashMap<K,V>();
- }
- public static <K, V> Map<K,V> newHashMap() {
- return new LinkedHashMap<K,V>();
- }
- public <T> List<T> newList(Collection<T> t) {
- return new ArrayList<T>(t);
- }
- public <T> Set<T> newSet(Collection<T> t) {
- return new TreeSet<T>(t);
- }
- public <K, V> Map<K,V> newMap(Map<K,V> t) {
- return new LinkedHashMap<K,V>(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<File>();
- 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<String> 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<String> 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<Map.Entry<Object,Object>> i = p.entrySet().iterator(); i.hasNext();) {
- Map.Entry<Object,Object> 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<String> getPropertyKeys(boolean inherit) {
- Set<String> 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<Object> 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 <T> String join(T list[]) {
- return join(list, ",");
- }
- public static void split(String s, Collection<String> set) {
- String elements[] = s.trim().split(LIST_SPLITTER);
- for (String element : elements) {
- if (element.length() > 0)
- set.add(element);
- }
- }
- public static Collection<String> split(String s) {
- return split(s, LIST_SPLITTER);
- }
- public static Collection<String> 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<String> result = new ArrayList<String>();
- 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<String> missed = Create.set();
- if (pattern != null) {
- for (String p : pattern) {
- boolean match = false;
- Pattern pat = Pattern.compile(p);
- for (Iterator<String> i = errors.iterator(); i.hasNext();) {
- if (pat.matcher(i.next()).find()) {
- i.remove();
- match = true;
- }
- }
- for (Iterator<String> 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<String> 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.
- *
- * <pre>
- * "/" + "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 <key>=<name> ", 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<String> 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);
- }
-
- /**
- * <p>
- * 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:
- * </p>
- *
- * <pre>
- * 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"
- * </pre>
- *
- * @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<String>=\"").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<String>=\"").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<File> 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<String> iterator() {
- Set<String> keys = keySet();
- final Iterator<String> it = keys.iterator();
- return new Iterator<String>() {
- String current;
- public boolean hasNext() {
- return it.hasNext();
- }
- public String next() {
- return current = it.next().toString();
- }
- public void remove() {
- getProperties().remove(current);
- }
- };
- }
- public Set<String> keySet() {
- Set<String> 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<Location> locations = new ArrayList<Location>();
- 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;
- }
- }