PageRenderTime 60ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/biz.aQute.bndlib/src/aQute/bnd/osgi/Processor.java

https://github.com/carrot-garden/osgi_bnd
Java | 1780 lines | 1226 code | 243 blank | 311 comment | 274 complexity | 4234efc78b77de16b6189de56bda79f7 MD5 | raw file
Possible License(s): Apache-2.0
  1. package aQute.bnd.osgi;
  2. import java.io.*;
  3. import java.net.*;
  4. import java.util.*;
  5. import java.util.Map.Entry;
  6. import java.util.concurrent.*;
  7. import java.util.jar.*;
  8. import java.util.regex.*;
  9. import aQute.bnd.header.*;
  10. import aQute.bnd.service.*;
  11. import aQute.bnd.version.*;
  12. import aQute.lib.collections.*;
  13. import aQute.lib.io.*;
  14. import aQute.libg.generics.*;
  15. import aQute.service.reporter.*;
  16. public class Processor extends Domain implements Reporter, Registry, Constants, Closeable {
  17. static ThreadLocal<Processor> current = new ThreadLocal<Processor>();
  18. static ExecutorService executor = Executors.newCachedThreadPool();
  19. static Random random = new Random();
  20. // TODO handle include files out of date
  21. // TODO make splitter skip eagerly whitespace so trim is not necessary
  22. public final static String LIST_SPLITTER = "\\s*,\\s*";
  23. final List<String> errors = new ArrayList<String>();
  24. final List<String> warnings = new ArrayList<String>();
  25. final Set<Object> basicPlugins = new HashSet<Object>();
  26. private final Set<Closeable> toBeClosed = new HashSet<Closeable>();
  27. Set<Object> plugins;
  28. boolean pedantic;
  29. boolean trace;
  30. boolean exceptions;
  31. boolean fileMustExist = true;
  32. private File base = new File("").getAbsoluteFile();
  33. Properties properties;
  34. String profile;
  35. private Macro replacer;
  36. private long lastModified;
  37. private File propertiesFile;
  38. private boolean fixup = true;
  39. long modified;
  40. Processor parent;
  41. List<File> included;
  42. CL pluginLoader;
  43. Collection<String> filter;
  44. HashSet<String> missingCommand;
  45. public Processor() {
  46. properties = new Properties();
  47. }
  48. public Processor(Properties parent) {
  49. properties = new Properties(parent);
  50. }
  51. public Processor(Processor child) {
  52. this(child.properties);
  53. this.parent = child;
  54. }
  55. public void setParent(Processor processor) {
  56. this.parent = processor;
  57. Properties ext = new Properties(processor.properties);
  58. ext.putAll(this.properties);
  59. this.properties = ext;
  60. }
  61. public Processor getParent() {
  62. return parent;
  63. }
  64. public Processor getTop() {
  65. if (parent == null)
  66. return this;
  67. return parent.getTop();
  68. }
  69. public void getInfo(Reporter processor, String prefix) {
  70. if (isFailOk())
  71. addAll(warnings, processor.getErrors(), prefix);
  72. else
  73. addAll(errors, processor.getErrors(), prefix);
  74. addAll(warnings, processor.getWarnings(), prefix);
  75. processor.getErrors().clear();
  76. processor.getWarnings().clear();
  77. }
  78. public void getInfo(Reporter processor) {
  79. getInfo(processor, "");
  80. }
  81. private <T> void addAll(List<String> to, List< ? extends T> from, String prefix) {
  82. for (T x : from) {
  83. to.add(prefix + x);
  84. }
  85. }
  86. /**
  87. * A processor can mark itself current for a thread.
  88. *
  89. * @return
  90. */
  91. private Processor current() {
  92. Processor p = current.get();
  93. if (p == null)
  94. return this;
  95. return p;
  96. }
  97. public SetLocation warning(String string, Object... args) {
  98. Processor p = current();
  99. String s = formatArrays(string, args);
  100. if (!p.warnings.contains(s))
  101. p.warnings.add(s);
  102. p.signal();
  103. return location(s);
  104. }
  105. public SetLocation error(String string, Object... args) {
  106. Processor p = current();
  107. try {
  108. if (p.isFailOk())
  109. return p.warning(string, args);
  110. String s = formatArrays(string, args == null ? new Object[0] : args);
  111. if (!p.errors.contains(s))
  112. p.errors.add(s);
  113. return location(s);
  114. }
  115. finally {
  116. p.signal();
  117. }
  118. }
  119. public void progress(float progress, String format, Object... args) {
  120. format = String.format("[%2d] %s", (int) progress, format);
  121. trace(format, args);
  122. }
  123. public void progress(String format, Object... args) {
  124. progress(-1f, format, args);
  125. }
  126. public SetLocation exception(Throwable t, String format, Object... args) {
  127. return error(format, t, args);
  128. }
  129. public SetLocation error(String string, Throwable t, Object... args) {
  130. Processor p = current();
  131. try {
  132. if (p.exceptions)
  133. t.printStackTrace();
  134. if (p.isFailOk()) {
  135. return p.warning(string + ": " + t, args);
  136. }
  137. p.errors.add("Exception: " + t.getMessage());
  138. String s = formatArrays(string, args == null ? new Object[0] : args);
  139. if (!p.errors.contains(s))
  140. p.errors.add(s);
  141. return location(s);
  142. }
  143. finally {
  144. p.signal();
  145. }
  146. }
  147. public void signal() {}
  148. public List<String> getWarnings() {
  149. return warnings;
  150. }
  151. public List<String> getErrors() {
  152. return errors;
  153. }
  154. /**
  155. * Standard OSGi header parser.
  156. *
  157. * @param value
  158. * @return
  159. */
  160. static public Parameters parseHeader(String value, Processor logger) {
  161. return new Parameters(value, logger);
  162. }
  163. public Parameters parseHeader(String value) {
  164. return new Parameters(value, this);
  165. }
  166. public void addClose(Closeable jar) {
  167. assert jar != null;
  168. toBeClosed.add(jar);
  169. }
  170. public void removeClose(Closeable jar) {
  171. assert jar != null;
  172. toBeClosed.remove(jar);
  173. }
  174. public boolean isPedantic() {
  175. return current().pedantic;
  176. }
  177. public void setPedantic(boolean pedantic) {
  178. this.pedantic = pedantic;
  179. }
  180. public void use(Processor reporter) {
  181. setPedantic(reporter.isPedantic());
  182. setTrace(reporter.isTrace());
  183. setBase(reporter.getBase());
  184. setFailOk(reporter.isFailOk());
  185. }
  186. public static File getFile(File base, String file) {
  187. return IO.getFile(base, file);
  188. }
  189. public File getFile(String file) {
  190. return getFile(base, file);
  191. }
  192. /**
  193. * Return a list of plugins that implement the given class.
  194. *
  195. * @param clazz
  196. * Each returned plugin implements this class/interface
  197. * @return A list of plugins
  198. */
  199. public <T> List<T> getPlugins(Class<T> clazz) {
  200. List<T> l = new ArrayList<T>();
  201. Set<Object> all = getPlugins();
  202. for (Object plugin : all) {
  203. if (clazz.isInstance(plugin))
  204. l.add(clazz.cast(plugin));
  205. }
  206. return l;
  207. }
  208. /**
  209. * Returns the first plugin it can find of the given type.
  210. *
  211. * @param <T>
  212. * @param clazz
  213. * @return
  214. */
  215. public <T> T getPlugin(Class<T> clazz) {
  216. Set<Object> all = getPlugins();
  217. for (Object plugin : all) {
  218. if (clazz.isInstance(plugin))
  219. return clazz.cast(plugin);
  220. }
  221. return null;
  222. }
  223. /**
  224. * Return a list of plugins. Plugins are defined with the -plugin command.
  225. * They are class names, optionally associated with attributes. Plugins can
  226. * implement the Plugin interface to see these attributes. Any object can be
  227. * a plugin.
  228. *
  229. * @return
  230. */
  231. protected synchronized Set<Object> getPlugins() {
  232. if (this.plugins != null)
  233. return this.plugins;
  234. missingCommand = new HashSet<String>();
  235. Set<Object> list = new LinkedHashSet<Object>();
  236. // The owner of the plugin is always in there.
  237. list.add(this);
  238. setTypeSpecificPlugins(list);
  239. if (parent != null)
  240. list.addAll(parent.getPlugins());
  241. // We only use plugins now when they are defined on our level
  242. // and not if it is in our parent. We inherit from our parent
  243. // through the previous block.
  244. if (properties.containsKey(PLUGIN)) {
  245. String spe = getProperty(PLUGIN);
  246. if (spe.equals(NONE))
  247. return new LinkedHashSet<Object>();
  248. String pluginPath = getProperty(PLUGINPATH);
  249. loadPlugins(list, spe, pluginPath);
  250. }
  251. return this.plugins = list;
  252. }
  253. /**
  254. * @param list
  255. * @param spe
  256. */
  257. protected void loadPlugins(Set<Object> list, String spe, String pluginPath) {
  258. Parameters plugins = new Parameters(spe);
  259. CL loader = getLoader();
  260. // First add the plugin-specific paths from their path: directives
  261. for (Entry<String,Attrs> entry : plugins.entrySet()) {
  262. String key = removeDuplicateMarker(entry.getKey());
  263. String path = entry.getValue().get(PATH_DIRECTIVE);
  264. if (path != null) {
  265. String parts[] = path.split("\\s*,\\s*");
  266. try {
  267. for (String p : parts) {
  268. File f = getFile(p).getAbsoluteFile();
  269. loader.add(f.toURI().toURL());
  270. }
  271. }
  272. catch (Exception e) {
  273. error("Problem adding path %s to loader for plugin %s. Exception: (%s)", path, key, e);
  274. }
  275. }
  276. }
  277. // Next add -pluginpath entries
  278. if (pluginPath != null && pluginPath.length() > 0) {
  279. StringTokenizer tokenizer = new StringTokenizer(pluginPath, ",");
  280. while (tokenizer.hasMoreTokens()) {
  281. String path = tokenizer.nextToken().trim();
  282. try {
  283. File f = getFile(path).getAbsoluteFile();
  284. loader.add(f.toURI().toURL());
  285. }
  286. catch (Exception e) {
  287. error("Problem adding path %s from global plugin path. Exception: %s", path, e);
  288. }
  289. }
  290. }
  291. // Load the plugins
  292. for (Entry<String,Attrs> entry : plugins.entrySet()) {
  293. String key = entry.getKey();
  294. try {
  295. trace("Using plugin %s", key);
  296. // Plugins could use the same class with different
  297. // parameters so we could have duplicate names Remove
  298. // the ! added by the parser to make each name unique.
  299. key = removeDuplicateMarker(key);
  300. try {
  301. Class< ? > c = loader.loadClass(key);
  302. Object plugin = c.newInstance();
  303. customize(plugin, entry.getValue());
  304. if (plugin instanceof Closeable) {
  305. addClose((Closeable) plugin);
  306. }
  307. list.add(plugin);
  308. }
  309. catch (Throwable t) {
  310. // We can defer the error if the plugin specifies
  311. // a command name. In that case, we'll verify that
  312. // a bnd file does not contain any references to a
  313. // plugin
  314. // command. The reason this feature was added was
  315. // to compile plugin classes with the same build.
  316. String commands = entry.getValue().get(COMMAND_DIRECTIVE);
  317. if (commands == null)
  318. error("Problem loading the plugin: %s exception: (%s)", key, t);
  319. else {
  320. Collection<String> cs = split(commands);
  321. missingCommand.addAll(cs);
  322. }
  323. }
  324. }
  325. catch (Throwable e) {
  326. error("Problem loading the plugin: %s exception: (%s)", key, e);
  327. }
  328. }
  329. }
  330. protected void setTypeSpecificPlugins(Set<Object> list) {
  331. list.add(executor);
  332. list.add(random);
  333. list.addAll(basicPlugins);
  334. }
  335. /**
  336. * @param plugin
  337. * @param entry
  338. */
  339. protected <T> T customize(T plugin, Attrs map) {
  340. if (plugin instanceof Plugin) {
  341. if (map != null)
  342. ((Plugin) plugin).setProperties(map);
  343. ((Plugin) plugin).setReporter(this);
  344. }
  345. if (plugin instanceof RegistryPlugin) {
  346. ((RegistryPlugin) plugin).setRegistry(this);
  347. }
  348. return plugin;
  349. }
  350. @Override
  351. public boolean isFailOk() {
  352. String v = getProperty(Analyzer.FAIL_OK, null);
  353. return v != null && v.equalsIgnoreCase("true");
  354. }
  355. public File getBase() {
  356. return base;
  357. }
  358. public void setBase(File base) {
  359. this.base = base;
  360. }
  361. public void clear() {
  362. errors.clear();
  363. warnings.clear();
  364. }
  365. public void trace(String msg, Object... parms) {
  366. Processor p = current();
  367. if (p.trace) {
  368. System.err.printf("# " + msg + "%n", parms);
  369. }
  370. }
  371. public <T> List<T> newList() {
  372. return new ArrayList<T>();
  373. }
  374. public <T> Set<T> newSet() {
  375. return new TreeSet<T>();
  376. }
  377. public static <K, V> Map<K,V> newMap() {
  378. return new LinkedHashMap<K,V>();
  379. }
  380. public static <K, V> Map<K,V> newHashMap() {
  381. return new LinkedHashMap<K,V>();
  382. }
  383. public <T> List<T> newList(Collection<T> t) {
  384. return new ArrayList<T>(t);
  385. }
  386. public <T> Set<T> newSet(Collection<T> t) {
  387. return new TreeSet<T>(t);
  388. }
  389. public <K, V> Map<K,V> newMap(Map<K,V> t) {
  390. return new LinkedHashMap<K,V>(t);
  391. }
  392. public void close() {
  393. for (Closeable c : toBeClosed) {
  394. try {
  395. c.close();
  396. }
  397. catch (IOException e) {
  398. // Who cares?
  399. }
  400. }
  401. toBeClosed.clear();
  402. }
  403. public String _basedir(@SuppressWarnings("unused")
  404. String args[]) {
  405. if (base == null)
  406. throw new IllegalArgumentException("No base dir set");
  407. return base.getAbsolutePath();
  408. }
  409. /**
  410. * Property handling ...
  411. *
  412. * @return
  413. */
  414. public Properties getProperties() {
  415. if (fixup) {
  416. fixup = false;
  417. begin();
  418. }
  419. return properties;
  420. }
  421. public String getProperty(String key) {
  422. return getProperty(key, null);
  423. }
  424. public void mergeProperties(File file, boolean override) {
  425. if (file.isFile()) {
  426. try {
  427. Properties properties = loadProperties(file);
  428. mergeProperties(properties, override);
  429. }
  430. catch (Exception e) {
  431. error("Error loading properties file: " + file);
  432. }
  433. } else {
  434. if (!file.exists())
  435. error("Properties file does not exist: " + file);
  436. else
  437. error("Properties file must a file, not a directory: " + file);
  438. }
  439. }
  440. public void mergeProperties(Properties properties, boolean override) {
  441. for (Enumeration< ? > e = properties.propertyNames(); e.hasMoreElements();) {
  442. String key = (String) e.nextElement();
  443. String value = properties.getProperty(key);
  444. if (override || !getProperties().containsKey(key))
  445. setProperty(key, value);
  446. }
  447. }
  448. public void setProperties(Properties properties) {
  449. doIncludes(getBase(), properties);
  450. this.properties.putAll(properties);
  451. }
  452. public void addProperties(File file) throws Exception {
  453. addIncluded(file);
  454. Properties p = loadProperties(file);
  455. setProperties(p);
  456. }
  457. public void addProperties(Map< ? , ? > properties) {
  458. for (Entry< ? , ? > entry : properties.entrySet()) {
  459. setProperty(entry.getKey().toString(), entry.getValue() + "");
  460. }
  461. }
  462. public synchronized void addIncluded(File file) {
  463. if (included == null)
  464. included = new ArrayList<File>();
  465. included.add(file);
  466. }
  467. /**
  468. * Inspect the properties and if you find -includes parse the line included
  469. * manifest files or properties files. The files are relative from the given
  470. * base, this is normally the base for the analyzer.
  471. *
  472. * @param ubase
  473. * @param p
  474. * @param done
  475. * @throws IOException
  476. * @throws IOException
  477. */
  478. private void doIncludes(File ubase, Properties p) {
  479. String includes = p.getProperty(INCLUDE);
  480. if (includes != null) {
  481. includes = getReplacer().process(includes);
  482. p.remove(INCLUDE);
  483. Collection<String> clauses = new Parameters(includes).keySet();
  484. for (String value : clauses) {
  485. boolean fileMustExist = true;
  486. boolean overwrite = true;
  487. while (true) {
  488. if (value.startsWith("-")) {
  489. fileMustExist = false;
  490. value = value.substring(1).trim();
  491. } else if (value.startsWith("~")) {
  492. // Overwrite properties!
  493. overwrite = false;
  494. value = value.substring(1).trim();
  495. } else
  496. break;
  497. }
  498. try {
  499. File file = getFile(ubase, value).getAbsoluteFile();
  500. if (!file.isFile() && fileMustExist) {
  501. error("Included file " + file + (file.exists() ? " does not exist" : " is directory"));
  502. } else
  503. doIncludeFile(file, overwrite, p);
  504. }
  505. catch (Exception e) {
  506. if (fileMustExist)
  507. error("Error in processing included file: " + value, e);
  508. }
  509. }
  510. }
  511. }
  512. /**
  513. * @param file
  514. * @param parent
  515. * @param done
  516. * @param overwrite
  517. * @throws FileNotFoundException
  518. * @throws IOException
  519. */
  520. public void doIncludeFile(File file, boolean overwrite, Properties target) throws Exception {
  521. doIncludeFile(file, overwrite, target, null);
  522. }
  523. /**
  524. * @param file
  525. * @param parent
  526. * @param done
  527. * @param overwrite
  528. * @param extensionName
  529. * @throws FileNotFoundException
  530. * @throws IOException
  531. */
  532. public void doIncludeFile(File file, boolean overwrite, Properties target, String extensionName) throws Exception {
  533. if (included != null && included.contains(file)) {
  534. error("Cyclic or multiple include of " + file);
  535. } else {
  536. addIncluded(file);
  537. updateModified(file.lastModified(), file.toString());
  538. InputStream in = new FileInputStream(file);
  539. try {
  540. Properties sub;
  541. if (file.getName().toLowerCase().endsWith(".mf")) {
  542. sub = getManifestAsProperties(in);
  543. } else
  544. sub = loadProperties(in, file.getAbsolutePath());
  545. doIncludes(file.getParentFile(), sub);
  546. // make sure we do not override properties
  547. for (Map.Entry< ? , ? > entry : sub.entrySet()) {
  548. String key = (String) entry.getKey();
  549. String value = (String) entry.getValue();
  550. if (overwrite || !target.containsKey(key)) {
  551. target.setProperty(key, value);
  552. } else if (extensionName != null) {
  553. String extensionKey = extensionName + "." + key;
  554. if (!target.containsKey(extensionKey))
  555. target.setProperty(extensionKey, value);
  556. }
  557. }
  558. }
  559. finally {
  560. IO.close(in);
  561. }
  562. }
  563. }
  564. public void unsetProperty(String string) {
  565. getProperties().remove(string);
  566. }
  567. public boolean refresh() {
  568. plugins = null; // We always refresh our plugins
  569. if (propertiesFile == null)
  570. return false;
  571. boolean changed = updateModified(propertiesFile.lastModified(), "properties file");
  572. if (included != null) {
  573. for (File file : included) {
  574. if (changed)
  575. break;
  576. changed |= !file.exists() || updateModified(file.lastModified(), "include file: " + file);
  577. }
  578. }
  579. profile = getProperty(PROFILE); // Used in property access
  580. if (changed) {
  581. forceRefresh();
  582. return true;
  583. }
  584. return false;
  585. }
  586. /**
  587. *
  588. */
  589. public void forceRefresh() {
  590. included = null;
  591. properties.clear();
  592. setProperties(propertiesFile, base);
  593. propertiesChanged();
  594. }
  595. public void propertiesChanged() {}
  596. /**
  597. * Set the properties by file. Setting the properties this way will also set
  598. * the base for this analyzer. After reading the properties, this will call
  599. * setProperties(Properties) which will handle the includes.
  600. *
  601. * @param propertiesFile
  602. * @throws FileNotFoundException
  603. * @throws IOException
  604. */
  605. public void setProperties(File propertiesFile) throws IOException {
  606. propertiesFile = propertiesFile.getAbsoluteFile();
  607. setProperties(propertiesFile, propertiesFile.getParentFile());
  608. }
  609. public void setProperties(File propertiesFile, File base) {
  610. this.propertiesFile = propertiesFile.getAbsoluteFile();
  611. setBase(base);
  612. try {
  613. if (propertiesFile.isFile()) {
  614. // System.err.println("Loading properties " + propertiesFile);
  615. long modified = propertiesFile.lastModified();
  616. if (modified > System.currentTimeMillis() + 100) {
  617. System.err.println("Huh? This is in the future " + propertiesFile);
  618. this.modified = System.currentTimeMillis();
  619. } else
  620. this.modified = modified;
  621. included = null;
  622. Properties p = loadProperties(propertiesFile);
  623. setProperties(p);
  624. } else {
  625. if (fileMustExist) {
  626. error("No such properties file: " + propertiesFile);
  627. }
  628. }
  629. }
  630. catch (IOException e) {
  631. error("Could not load properties " + propertiesFile);
  632. }
  633. }
  634. protected void begin() {
  635. if (isTrue(getProperty(PEDANTIC)))
  636. setPedantic(true);
  637. }
  638. public static boolean isTrue(String value) {
  639. if (value == null)
  640. return false;
  641. return !"false".equalsIgnoreCase(value);
  642. }
  643. /**
  644. * Get a property without preprocessing it with a proper default
  645. *
  646. * @param headerName
  647. * @param deflt
  648. * @return
  649. */
  650. public String getUnprocessedProperty(String key, String deflt) {
  651. return getProperties().getProperty(key, deflt);
  652. }
  653. /**
  654. * Get a property with preprocessing it with a proper default
  655. *
  656. * @param headerName
  657. * @param deflt
  658. * @return
  659. */
  660. public String getProperty(String key, String deflt) {
  661. String value = null;
  662. Instruction ins = new Instruction(key);
  663. if (!ins.isLiteral()) {
  664. // Handle a wildcard key, make sure they're sorted
  665. // for consistency
  666. SortedList<String> sortedList = SortedList.fromIterator(iterator());
  667. StringBuilder sb = new StringBuilder();
  668. String del = "";
  669. for (String k : sortedList) {
  670. if (ins.matches(k)) {
  671. String v = getProperty(k, null);
  672. if (v != null) {
  673. sb.append(del);
  674. del = ",";
  675. sb.append(v);
  676. }
  677. }
  678. }
  679. if (sb.length() == 0)
  680. return deflt;
  681. return sb.toString();
  682. }
  683. Processor source = this;
  684. // Use the key as is first, if found ok
  685. if (filter != null && filter.contains(key)) {
  686. value = (String) getProperties().get(key);
  687. } else {
  688. while (source != null) {
  689. value = (String) source.getProperties().get(key);
  690. if (value != null)
  691. break;
  692. source = source.getParent();
  693. }
  694. }
  695. // Check if we found a value, if not, try to prefix
  696. // it with a profile if found and search again. profiles
  697. // are a simple name that is prefixed like [profile]. This
  698. // allows different variables to be used in different profiles.
  699. if (value == null && profile != null) {
  700. String pkey = "[" + profile + "]" + key;
  701. if (filter != null && filter.contains(key)) {
  702. value = (String) getProperties().get(pkey);
  703. } else {
  704. while (source != null) {
  705. value = (String) source.getProperties().get(pkey);
  706. if (value != null)
  707. break;
  708. source = source.getParent();
  709. }
  710. }
  711. }
  712. if (value != null)
  713. return getReplacer().process(value, source);
  714. else if (deflt != null)
  715. return getReplacer().process(deflt, this);
  716. else
  717. return null;
  718. }
  719. /**
  720. * Helper to load a properties file from disk.
  721. *
  722. * @param file
  723. * @return
  724. * @throws IOException
  725. */
  726. public Properties loadProperties(File file) throws IOException {
  727. updateModified(file.lastModified(), "Properties file: " + file);
  728. InputStream in = new FileInputStream(file);
  729. try {
  730. Properties p = loadProperties(in, file.getAbsolutePath());
  731. return p;
  732. }
  733. finally {
  734. in.close();
  735. }
  736. }
  737. Properties loadProperties(InputStream in, String name) throws IOException {
  738. int n = name.lastIndexOf('/');
  739. if (n > 0)
  740. name = name.substring(0, n);
  741. if (name.length() == 0)
  742. name = ".";
  743. try {
  744. Properties p = new Properties();
  745. p.load(in);
  746. return replaceAll(p, "\\$\\{\\.\\}", name);
  747. }
  748. catch (Exception e) {
  749. error("Error during loading properties file: " + name + ", error:" + e);
  750. return new Properties();
  751. }
  752. }
  753. /**
  754. * Replace a string in all the values of the map. This can be used to
  755. * preassign variables that change. I.e. the base directory ${.} for a
  756. * loaded properties
  757. */
  758. public static Properties replaceAll(Properties p, String pattern, String replacement) {
  759. Properties result = new Properties();
  760. for (Iterator<Map.Entry<Object,Object>> i = p.entrySet().iterator(); i.hasNext();) {
  761. Map.Entry<Object,Object> entry = i.next();
  762. String key = (String) entry.getKey();
  763. String value = (String) entry.getValue();
  764. value = value.replaceAll(pattern, replacement);
  765. result.put(key, value);
  766. }
  767. return result;
  768. }
  769. /**
  770. * Print a standard Map based OSGi header.
  771. *
  772. * @param exports
  773. * map { name => Map { attribute|directive => value } }
  774. * @return the clauses
  775. * @throws IOException
  776. */
  777. public static String printClauses(Map< ? , ? extends Map< ? , ? >> exports) throws IOException {
  778. return printClauses(exports, false);
  779. }
  780. public static String printClauses(Map< ? , ? extends Map< ? , ? >> exports, @SuppressWarnings("unused")
  781. boolean checkMultipleVersions) throws IOException {
  782. StringBuilder sb = new StringBuilder();
  783. String del = "";
  784. for (Entry< ? , ? extends Map< ? , ? >> entry : exports.entrySet()) {
  785. String name = entry.getKey().toString();
  786. Map< ? , ? > clause = entry.getValue();
  787. // We allow names to be duplicated in the input
  788. // by ending them with '~'. This is necessary to use
  789. // the package names as keys. However, we remove these
  790. // suffixes in the output so that you can set multiple
  791. // exports with different attributes.
  792. String outname = removeDuplicateMarker(name);
  793. sb.append(del);
  794. sb.append(outname);
  795. printClause(clause, sb);
  796. del = ",";
  797. }
  798. return sb.toString();
  799. }
  800. public static void printClause(Map< ? , ? > map, StringBuilder sb) throws IOException {
  801. for (Entry< ? , ? > entry : map.entrySet()) {
  802. Object key = entry.getKey();
  803. // Skip directives we do not recognize
  804. if (key.equals(NO_IMPORT_DIRECTIVE) || key.equals(PROVIDE_DIRECTIVE) || key.equals(SPLIT_PACKAGE_DIRECTIVE)
  805. || key.equals(FROM_DIRECTIVE))
  806. continue;
  807. String value = ((String) entry.getValue()).trim();
  808. sb.append(";");
  809. sb.append(key);
  810. sb.append("=");
  811. quote(sb, value);
  812. }
  813. }
  814. /**
  815. * @param sb
  816. * @param value
  817. * @return
  818. * @throws IOException
  819. */
  820. public static boolean quote(Appendable sb, String value) throws IOException {
  821. boolean clean = (value.length() >= 2 && value.charAt(0) == '"' && value.charAt(value.length() - 1) == '"')
  822. || Verifier.TOKEN.matcher(value).matches();
  823. if (!clean)
  824. sb.append("\"");
  825. sb.append(value);
  826. if (!clean)
  827. sb.append("\"");
  828. return clean;
  829. }
  830. public Macro getReplacer() {
  831. if (replacer == null)
  832. return replacer = new Macro(this, getMacroDomains());
  833. return replacer;
  834. }
  835. /**
  836. * This should be overridden by subclasses to add extra macro command
  837. * domains on the search list.
  838. *
  839. * @return
  840. */
  841. protected Object[] getMacroDomains() {
  842. return new Object[] {};
  843. }
  844. /**
  845. * Return the properties but expand all macros. This always returns a new
  846. * Properties object that can be used in any way.
  847. *
  848. * @return
  849. */
  850. public Properties getFlattenedProperties() {
  851. return getReplacer().getFlattenedProperties();
  852. }
  853. /**
  854. * Return all inherited property keys
  855. *
  856. * @return
  857. */
  858. public Set<String> getPropertyKeys(boolean inherit) {
  859. Set<String> result;
  860. if (parent == null || !inherit) {
  861. result = Create.set();
  862. } else
  863. result = parent.getPropertyKeys(inherit);
  864. for (Object o : properties.keySet())
  865. result.add(o.toString());
  866. return result;
  867. }
  868. public boolean updateModified(long time, @SuppressWarnings("unused")
  869. String reason) {
  870. if (time > lastModified) {
  871. lastModified = time;
  872. return true;
  873. }
  874. return false;
  875. }
  876. public long lastModified() {
  877. return lastModified;
  878. }
  879. /**
  880. * Add or override a new property.
  881. *
  882. * @param key
  883. * @param value
  884. */
  885. public void setProperty(String key, String value) {
  886. checkheader: for (int i = 0; i < headers.length; i++) {
  887. if (headers[i].equalsIgnoreCase(value)) {
  888. value = headers[i];
  889. break checkheader;
  890. }
  891. }
  892. getProperties().put(key, value);
  893. }
  894. /**
  895. * Read a manifest but return a properties object.
  896. *
  897. * @param in
  898. * @return
  899. * @throws IOException
  900. */
  901. public static Properties getManifestAsProperties(InputStream in) throws IOException {
  902. Properties p = new Properties();
  903. Manifest manifest = new Manifest(in);
  904. for (Iterator<Object> it = manifest.getMainAttributes().keySet().iterator(); it.hasNext();) {
  905. Attributes.Name key = (Attributes.Name) it.next();
  906. String value = manifest.getMainAttributes().getValue(key);
  907. p.put(key.toString(), value);
  908. }
  909. return p;
  910. }
  911. public File getPropertiesFile() {
  912. return propertiesFile;
  913. }
  914. public void setFileMustExist(boolean mustexist) {
  915. fileMustExist = mustexist;
  916. }
  917. static public String read(InputStream in) throws Exception {
  918. InputStreamReader ir = new InputStreamReader(in, "UTF8");
  919. StringBuilder sb = new StringBuilder();
  920. try {
  921. char chars[] = new char[1000];
  922. int size = ir.read(chars);
  923. while (size > 0) {
  924. sb.append(chars, 0, size);
  925. size = ir.read(chars);
  926. }
  927. }
  928. finally {
  929. ir.close();
  930. }
  931. return sb.toString();
  932. }
  933. /**
  934. * Join a list.
  935. *
  936. * @param args
  937. * @return
  938. */
  939. public static String join(Collection< ? > list, String delimeter) {
  940. return join(delimeter, list);
  941. }
  942. public static String join(String delimeter, Collection< ? >... list) {
  943. StringBuilder sb = new StringBuilder();
  944. String del = "";
  945. if (list != null) {
  946. for (Collection< ? > l : list) {
  947. for (Object item : l) {
  948. sb.append(del);
  949. sb.append(item);
  950. del = delimeter;
  951. }
  952. }
  953. }
  954. return sb.toString();
  955. }
  956. public static String join(Object[] list, String delimeter) {
  957. if (list == null)
  958. return "";
  959. StringBuilder sb = new StringBuilder();
  960. String del = "";
  961. for (Object item : list) {
  962. sb.append(del);
  963. sb.append(item);
  964. del = delimeter;
  965. }
  966. return sb.toString();
  967. }
  968. public static String join(Collection< ? >... list) {
  969. return join(",", list);
  970. }
  971. public static <T> String join(T list[]) {
  972. return join(list, ",");
  973. }
  974. public static void split(String s, Collection<String> set) {
  975. String elements[] = s.trim().split(LIST_SPLITTER);
  976. for (String element : elements) {
  977. if (element.length() > 0)
  978. set.add(element);
  979. }
  980. }
  981. public static Collection<String> split(String s) {
  982. return split(s, LIST_SPLITTER);
  983. }
  984. public static Collection<String> split(String s, String splitter) {
  985. if (s != null)
  986. s = s.trim();
  987. if (s == null || s.trim().length() == 0)
  988. return Collections.emptyList();
  989. return Arrays.asList(s.split(splitter));
  990. }
  991. public static String merge(String... strings) {
  992. ArrayList<String> result = new ArrayList<String>();
  993. for (String s : strings) {
  994. if (s != null)
  995. split(s, result);
  996. }
  997. return join(result);
  998. }
  999. public boolean isExceptions() {
  1000. return exceptions;
  1001. }
  1002. public void setExceptions(boolean exceptions) {
  1003. this.exceptions = exceptions;
  1004. }
  1005. /**
  1006. * Make the file short if it is inside our base directory, otherwise long.
  1007. *
  1008. * @param f
  1009. * @return
  1010. */
  1011. public String normalize(String f) {
  1012. if (f.startsWith(base.getAbsolutePath() + "/"))
  1013. return f.substring(base.getAbsolutePath().length() + 1);
  1014. return f;
  1015. }
  1016. public String normalize(File f) {
  1017. return normalize(f.getAbsolutePath());
  1018. }
  1019. public static String removeDuplicateMarker(String key) {
  1020. int i = key.length() - 1;
  1021. while (i >= 0 && key.charAt(i) == DUPLICATE_MARKER)
  1022. --i;
  1023. return key.substring(0, i + 1);
  1024. }
  1025. public static boolean isDuplicate(String name) {
  1026. return name.length() > 0 && name.charAt(name.length() - 1) == DUPLICATE_MARKER;
  1027. }
  1028. public void setTrace(boolean x) {
  1029. trace = x;
  1030. }
  1031. static class CL extends URLClassLoader {
  1032. CL() {
  1033. super(new URL[0], Processor.class.getClassLoader());
  1034. }
  1035. void add(URL url) {
  1036. URL urls[] = getURLs();
  1037. for (URL u : urls) {
  1038. if (u.equals(url))
  1039. return;
  1040. }
  1041. super.addURL(url);
  1042. }
  1043. @Override
  1044. public Class< ? > loadClass(String name) throws NoClassDefFoundError {
  1045. try {
  1046. Class< ? > c = super.loadClass(name);
  1047. return c;
  1048. }
  1049. catch (Throwable t) {
  1050. StringBuilder sb = new StringBuilder();
  1051. sb.append(name);
  1052. sb.append(" not found, parent: ");
  1053. sb.append(getParent());
  1054. sb.append(" urls:");
  1055. sb.append(Arrays.toString(getURLs()));
  1056. sb.append(" exception:");
  1057. sb.append(t);
  1058. throw new NoClassDefFoundError(sb.toString());
  1059. }
  1060. }
  1061. }
  1062. private CL getLoader() {
  1063. if (pluginLoader == null) {
  1064. pluginLoader = new CL();
  1065. }
  1066. return pluginLoader;
  1067. }
  1068. /*
  1069. * Check if this is a valid project.
  1070. */
  1071. public boolean exists() {
  1072. return base != null && base.isDirectory() && propertiesFile != null && propertiesFile.isFile();
  1073. }
  1074. public boolean isOk() {
  1075. return isFailOk() || (getErrors().size() == 0);
  1076. }
  1077. public boolean check(String... pattern) throws IOException {
  1078. Set<String> missed = Create.set();
  1079. if (pattern != null) {
  1080. for (String p : pattern) {
  1081. boolean match = false;
  1082. Pattern pat = Pattern.compile(p);
  1083. for (Iterator<String> i = errors.iterator(); i.hasNext();) {
  1084. if (pat.matcher(i.next()).find()) {
  1085. i.remove();
  1086. match = true;
  1087. }
  1088. }
  1089. for (Iterator<String> i = warnings.iterator(); i.hasNext();) {
  1090. if (pat.matcher(i.next()).find()) {
  1091. i.remove();
  1092. match = true;
  1093. }
  1094. }
  1095. if (!match)
  1096. missed.add(p);
  1097. }
  1098. }
  1099. if (missed.isEmpty() && isPerfect())
  1100. return true;
  1101. if (!missed.isEmpty())
  1102. System.err.println("Missed the following patterns in the warnings or errors: " + missed);
  1103. report(System.err);
  1104. return false;
  1105. }
  1106. protected void report(Appendable out) throws IOException {
  1107. if (errors.size() > 0) {
  1108. out.append(String.format("-----------------%nErrors%n"));
  1109. for (int i = 0; i < errors.size(); i++) {
  1110. out.append(String.format("%03d: %s%n", i, errors.get(i)));
  1111. }
  1112. }
  1113. if (warnings.size() > 0) {
  1114. out.append(String.format("-----------------%nWarnings%n"));
  1115. for (int i = 0; i < warnings.size(); i++) {
  1116. out.append(String.format("%03d: %s%n", i, warnings.get(i)));
  1117. }
  1118. }
  1119. }
  1120. public boolean isPerfect() {
  1121. return getErrors().size() == 0 && getWarnings().size() == 0;
  1122. }
  1123. public void setForceLocal(Collection<String> local) {
  1124. filter = local;
  1125. }
  1126. /**
  1127. * Answer if the name is a missing plugin's command name. If a bnd file
  1128. * contains the command name of a plugin, and that plugin is not available,
  1129. * then an error is reported during manifest calculation. This allows the
  1130. * plugin to fail to load when it is not needed. We first get the plugins to
  1131. * ensure it is properly initialized.
  1132. *
  1133. * @param name
  1134. * @return
  1135. */
  1136. public boolean isMissingPlugin(String name) {
  1137. getPlugins();
  1138. return missingCommand != null && missingCommand.contains(name);
  1139. }
  1140. /**
  1141. * Append two strings to for a path in a ZIP or JAR file. It is guaranteed
  1142. * to return a string that does not start, nor ends with a '/', while it is
  1143. * properly separated with slashes. Double slashes are properly removed.
  1144. *
  1145. * <pre>
  1146. * &quot;/&quot; + &quot;abc/def/&quot; becomes &quot;abc/def&quot;
  1147. *
  1148. * &#064;param prefix
  1149. * &#064;param suffix
  1150. * &#064;return
  1151. */
  1152. public static String appendPath(String... parts) {
  1153. StringBuilder sb = new StringBuilder();
  1154. boolean lastSlash = true;
  1155. for (String part : parts) {
  1156. for (int i = 0; i < part.length(); i++) {
  1157. char c = part.charAt(i);
  1158. if (c == '/') {
  1159. if (!lastSlash)
  1160. sb.append('/');
  1161. lastSlash = true;
  1162. } else {
  1163. sb.append(c);
  1164. lastSlash = false;
  1165. }
  1166. }
  1167. if (!lastSlash && sb.length() > 0) {
  1168. sb.append('/');
  1169. lastSlash = true;
  1170. }
  1171. }
  1172. if (lastSlash && sb.length() > 0)
  1173. sb.deleteCharAt(sb.length() - 1);
  1174. return sb.toString();
  1175. }
  1176. /**
  1177. * Parse the a=b strings and return a map of them.
  1178. *
  1179. * @param attrs
  1180. * @param clazz
  1181. * @return
  1182. */
  1183. public static Attrs doAttrbutes(Object[] attrs, Clazz clazz, Macro macro) {
  1184. Attrs map = new Attrs();
  1185. if (attrs == null || attrs.length == 0)
  1186. return map;
  1187. for (Object a : attrs) {
  1188. String attr = (String) a;
  1189. int n = attr.indexOf("=");
  1190. if (n > 0) {
  1191. map.put(attr.substring(0, n), macro.process(attr.substring(n + 1)));
  1192. } else
  1193. throw new IllegalArgumentException(formatArrays(
  1194. "Invalid attribute on package-info.java in %s , %s. Must be <key>=<name> ", clazz, attr));
  1195. }
  1196. return map;
  1197. }
  1198. /**
  1199. * This method is the same as String.format but it makes sure that any
  1200. * arrays are transformed to strings.
  1201. *
  1202. * @param string
  1203. * @param parms
  1204. * @return
  1205. */
  1206. public static String formatArrays(String string, Object... parms) {
  1207. Object[] parms2 = parms;
  1208. Object[] output = new Object[parms.length];
  1209. for (int i = 0; i < parms.length; i++) {
  1210. output[i] = makePrintable(parms[i]);
  1211. }
  1212. return String.format(string, parms2);
  1213. }
  1214. /**
  1215. * Check if the object is an array and turn it into a string if it is,
  1216. * otherwise unchanged.
  1217. *
  1218. * @param object
  1219. * the object to make printable
  1220. * @return a string if it was an array or the original object
  1221. */
  1222. public static Object makePrintable(Object object) {
  1223. if (object == null)
  1224. return object;
  1225. if (object.getClass().isArray()) {
  1226. Object[] array = (Object[]) object;
  1227. Object[] output = new Object[array.length];
  1228. for (int i = 0; i < array.length; i++) {
  1229. output[i] = makePrintable(array[i]);
  1230. }
  1231. return Arrays.toString(output);
  1232. }
  1233. return object;
  1234. }
  1235. public static String append(String... strings) {
  1236. List<String> result = Create.list();
  1237. for (String s : strings) {
  1238. result.addAll(split(s));
  1239. }
  1240. return join(result);
  1241. }
  1242. public synchronized Class< ? > getClass(String type, File jar) throws Exception {
  1243. CL cl = getLoader();
  1244. cl.add(jar.toURI().toURL());
  1245. return cl.loadClass(type);
  1246. }
  1247. public boolean isTrace() {
  1248. return current().trace;
  1249. }
  1250. public static long getDuration(String tm, long dflt) {
  1251. if (tm == null)
  1252. return dflt;
  1253. tm = tm.toUpperCase();
  1254. TimeUnit unit = TimeUnit.MILLISECONDS;
  1255. Matcher m = Pattern
  1256. .compile("\\s*(\\d+)\\s*(NANOSECONDS|MICROSECONDS|MILLISECONDS|SECONDS|MINUTES|HOURS|DAYS)?").matcher(
  1257. tm);
  1258. if (m.matches()) {
  1259. long duration = Long.parseLong(tm);
  1260. String u = m.group(2);
  1261. if (u != null)
  1262. unit = TimeUnit.valueOf(u);
  1263. duration = TimeUnit.MILLISECONDS.convert(duration, unit);
  1264. return duration;
  1265. }
  1266. return dflt;
  1267. }
  1268. /**
  1269. * Generate a random string, which is guaranteed to be a valid Java
  1270. * identifier (first character is an ASCII letter, subsequent characters are
  1271. * ASCII letters or numbers). Takes an optional parameter for the length of
  1272. * string to generate; default is 8 characters.
  1273. */
  1274. public String _random(String[] args) {
  1275. int numchars = 8;
  1276. if (args.length > 1) {
  1277. try {
  1278. numchars = Integer.parseInt(args[1]);
  1279. }
  1280. catch (NumberFormatException e) {
  1281. throw new IllegalArgumentException("Invalid character count parameter in ${random} macro.");
  1282. }
  1283. }
  1284. synchronized (Processor.class) {
  1285. if (random == null)
  1286. random = new Random();
  1287. }
  1288. char[] letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
  1289. char[] alphanums = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray();
  1290. char[] array = new char[numchars];
  1291. for (int i = 0; i < numchars; i++) {
  1292. char c;
  1293. if (i == 0)
  1294. c = letters[random.nextInt(letters.length)];
  1295. else
  1296. c = alphanums[random.nextInt(alphanums.length)];
  1297. array[i] = c;
  1298. }
  1299. return new String(array);
  1300. }
  1301. /**
  1302. * <p>
  1303. * Generates a Capability string, in the format specified by the OSGi
  1304. * Provide-Capability header, representing the current native platform
  1305. * according to OSGi RFC 188. For example on Windows7 running on an x86_64
  1306. * processor it should generate the following:
  1307. * </p>
  1308. *
  1309. * <pre>
  1310. * osgi.native;osgi.native.osname:List&lt;String&gt;="Windows7,Windows 7,Win32";osgi.native.osversion:Version=6.1.0;osgi.native.processor:List&lt;String&gt;="x86-64,amd64,em64t,x86_64"
  1311. * </pre>
  1312. *
  1313. * @param args
  1314. * Ignored; reserved for future use.
  1315. */
  1316. public String _native_capability(String[] args) {
  1317. StringBuilder builder = new StringBuilder().append("osgi.native");
  1318. try {
  1319. // Operating System name and version
  1320. String osnames;
  1321. Version osversion;
  1322. String sysPropOsName = System.getProperty("os.name");
  1323. String sysPropOsVersion = System.getProperty("os.version");
  1324. if (sysPropOsName.startsWith("Windows")) {
  1325. if (sysPropOsVersion.startsWith("6.2")) {
  1326. osversion = new Version(6,2,0);
  1327. osnames = "Windows8,Windows 8,Win32";
  1328. } else if (sysPropOsVersion.startsWith("6.1")) {
  1329. osversion = new Version(6, 1, 0);
  1330. osnames = "Windows7,Windows 7,Win32";
  1331. } else if (sysPropOsName.startsWith("6.0")) {
  1332. osversion = new Version(6, 0, 0);
  1333. osnames = "WindowsVista,WinVista,Windows Vista,Win32";
  1334. } else if (sysPropOsName.startsWith("5.1")) {
  1335. osversion = new Version(5, 1, 0);
  1336. osnames = "WindowsXP,WinXP,Windows XP,Win32";
  1337. } else {
  1338. throw new IllegalArgumentException(String.format("Unrecognised or unsupported Windows version while processing ${native} macro: %s version %s. Supported: XP, Vista, Win7, Win8.", sysPropOsName, sysPropOsVersion));
  1339. }
  1340. } else if (sysPropOsName.startsWith("Mac OS X")) {
  1341. osnames = "MacOSX,Mac OS X";
  1342. osversion = new Version(sysPropOsVersion);
  1343. } else if (sysPropOsName.toLowerCase().startsWith("linux")) {
  1344. osnames = "Linux";
  1345. osversion = new Version(sysPropOsVersion);
  1346. } else if (sysPropOsName.startsWith("Solaris")) {
  1347. osnames = "Solaris";
  1348. osversion = new Version(sysPropOsVersion);
  1349. } else if (sysPropOsName.startsWith("AIX")) {
  1350. osnames = "AIX";
  1351. osversion = new Version(sysPropOsVersion);
  1352. } else if (sysPropOsName.startsWith("HP-UX")) {
  1353. osnames = "HPUX,hp-ux";
  1354. osversion = new Version(sysPropOsVersion);
  1355. } else {
  1356. 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));
  1357. }
  1358. builder.append(";osgi.native.osname:List<String>=\"").append(osnames).append('"');
  1359. builder.append(";osgi.native.osversion:Version=").append(osversion.toString());
  1360. // Processor
  1361. String processorNames;
  1362. String arch = System.getProperty("os.arch");
  1363. if ("x86_64".equals(arch))
  1364. processorNames = "x86-64,amd64,em64t,x86_64";
  1365. else if ("x86".equals(arch))
  1366. processorNames = "x86,pentium,i386,i486,i586,i686";
  1367. else
  1368. throw new IllegalArgumentException(String.format("Unrecognised/unsupported processor name '%s' in ${native} macro.", arch));
  1369. builder.append(";osgi.native.processor:List<String>=\"").append(processorNames).append('"');
  1370. } catch (SecurityException e) {
  1371. throw new IllegalArgumentException("Security error retrieving system properties while processing ${native} macro.");
  1372. }
  1373. return builder.toString();
  1374. }
  1375. /**
  1376. * Set the current command thread. This must be balanced with the
  1377. * {@link #end(Processor)} method. The method returns the previous command
  1378. * owner or null. The command owner will receive all warnings and error
  1379. * reports.
  1380. */
  1381. protected Processor beginHandleErrors(String message) {
  1382. trace("begin %s", message);
  1383. Processor previous = current.get();
  1384. current.set(this);
  1385. return previous;
  1386. }
  1387. /**
  1388. * End a command. Will restore the previous command owner.
  1389. *
  1390. * @param previous
  1391. */
  1392. protected void endHandleErrors(Processor previous) {
  1393. trace("end");
  1394. current.set(previous);
  1395. }
  1396. public static Executor getExecutor() {
  1397. return executor;
  1398. }
  1399. /**
  1400. * These plugins are added to the total list of plugins. The separation is
  1401. * necessary because the list of plugins is refreshed now and then so we
  1402. * need to be able to add them at any moment in time.
  1403. *
  1404. * @param plugin
  1405. */
  1406. public synchronized void addBasicPlugin(Object plugin) {
  1407. basicPlugins.add(plugin);
  1408. if (plugins != null)
  1409. plugins.add(plugin);
  1410. }
  1411. public synchronized void removeBasicPlugin(Object plugin) {
  1412. basicPlugins.remove(plugin);
  1413. if (plugins != null)
  1414. plugins.remove(plugin);
  1415. }
  1416. public List<File> getIncluded() {
  1417. return included;
  1418. }
  1419. /**
  1420. * Overrides for the Domain class
  1421. */
  1422. @Override
  1423. public String get(String key) {
  1424. return getProperty(key);
  1425. }
  1426. @Override
  1427. public String get(String key, String deflt) {
  1428. return getProperty(key, deflt);
  1429. }
  1430. @Override
  1431. public void set(String key, String value) {
  1432. getProperties().setProperty(key, value);
  1433. }
  1434. @Override
  1435. public Iterator<String> iterator() {
  1436. Set<String> keys = keySet();
  1437. final Iterator<String> it = keys.iterator();
  1438. return new Iterator<String>() {
  1439. String current;
  1440. public boolean hasNext() {
  1441. return it.hasNext();
  1442. }
  1443. public String next() {
  1444. return current = it.next().toString();
  1445. }
  1446. public void remove() {
  1447. getProperties().remove(current);
  1448. }
  1449. };
  1450. }
  1451. public Set<String> keySet() {
  1452. Set<String> set;
  1453. if (parent == null)
  1454. set = Create.set();
  1455. else
  1456. set = parent.keySet();
  1457. for (Object o : properties.keySet())
  1458. set.add(o.toString());
  1459. return set;
  1460. }
  1461. /**
  1462. * Printout of the status of this processor for toString()
  1463. */
  1464. @Override
  1465. public String toString() {
  1466. try {
  1467. StringBuilder sb = new StringBuilder();
  1468. report(sb);
  1469. return sb.toString();
  1470. }
  1471. catch (Exception e) {
  1472. throw new RuntimeException(e);
  1473. }
  1474. }
  1475. /**
  1476. * Utiltity to replace an extension
  1477. *
  1478. * @param s
  1479. * @param extension
  1480. * @param newExtension
  1481. * @return
  1482. */
  1483. public String replaceExtension(String s, String extension, String newExtension) {
  1484. if (s.endsWith(extension))
  1485. s = s.substring(0, s.length() - extension.length());
  1486. return s + newExtension;
  1487. }
  1488. /**
  1489. * Create a location object and add it to the locations
  1490. *
  1491. * @param s
  1492. * @return
  1493. */
  1494. List<Location> locations = new ArrayList<Location>();
  1495. static class SetLocationImpl extends Location implements SetLocation {
  1496. public SetLocationImpl(String s) {
  1497. this.message = s;
  1498. }
  1499. public SetLocation file(String file) {
  1500. this.file = file;
  1501. return this;
  1502. }
  1503. public SetLocation header(String header) {
  1504. this.header = header;
  1505. return this;
  1506. }
  1507. public SetLocation context(String context) {
  1508. this.context = context;
  1509. return this;
  1510. }
  1511. public SetLocation method(String methodName) {
  1512. this.methodName = methodName;
  1513. return this;
  1514. }
  1515. public SetLocation line(int n) {
  1516. this.line = n;
  1517. return this;
  1518. }
  1519. public SetLocation reference(String reference) {
  1520. this.reference = reference;
  1521. return this;
  1522. }
  1523. }
  1524. private SetLocation location(String s) {
  1525. SetLocationImpl loc = new SetLocationImpl(s);
  1526. locations.add(loc);
  1527. return loc;
  1528. }
  1529. public Location getLocation(String msg) {
  1530. for (Location l : locations)
  1531. if ((l.message != null) && l.message.equals(msg))
  1532. return l;
  1533. return null;
  1534. }
  1535. }