Return-Path: Delivered-To: apmail-felix-commits-archive@www.apache.org Received: (qmail 98703 invoked from network); 13 Jul 2009 10:07:06 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.3) by minotaur.apache.org with SMTP; 13 Jul 2009 10:07:06 -0000 Received: (qmail 33961 invoked by uid 500); 13 Jul 2009 10:07:16 -0000 Delivered-To: apmail-felix-commits-archive@felix.apache.org Received: (qmail 33917 invoked by uid 500); 13 Jul 2009 10:07:16 -0000 Mailing-List: contact commits-help@felix.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@felix.apache.org Delivered-To: mailing list commits@felix.apache.org Received: (qmail 33908 invoked by uid 99); 13 Jul 2009 10:07:16 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 13 Jul 2009 10:07:16 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 13 Jul 2009 10:07:12 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 68DAE2388905; Mon, 13 Jul 2009 10:06:52 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r793527 [5/7] - in /felix/trunk/bundleplugin: ./ src/main/java/aQute/ src/main/java/aQute/bnd/ src/main/java/aQute/bnd/build/ src/main/java/aQute/bnd/help/ src/main/java/aQute/bnd/make/ src/main/java/aQute/bnd/service/ src/main/java/aQute/b... Date: Mon, 13 Jul 2009 10:06:50 -0000 To: commits@felix.apache.org From: mcculls@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20090713100652.68DAE2388905@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/FileResource.java URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/FileResource.java?rev=793527&view=auto ============================================================================== --- felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/FileResource.java (added) +++ felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/FileResource.java Mon Jul 13 10:06:47 2009 @@ -0,0 +1,86 @@ +/* Copyright 2006 aQute SARL + * Licensed under the Apache License, Version 2.0, see http://www.apache.org/licenses/LICENSE-2.0 */ +package aQute.lib.osgi; + +import java.io.*; +import java.util.regex.Pattern; + +public class FileResource implements Resource { + File file; + String extra; + + public FileResource(File file) { + this.file = file; + } + + public InputStream openInputStream() throws FileNotFoundException { + return new FileInputStream(file); + } + + public static void build(Jar jar, File directory, Pattern doNotCopy) { + traverse( + jar, + directory.getAbsolutePath().length(), + directory, + doNotCopy); + } + + public String toString() { + return ":" + file.getName() + ":"; + } + + public void write(OutputStream out) throws IOException { + copy(this, out); + } + + static synchronized void copy(Resource resource, OutputStream out) + throws IOException { + InputStream in = resource.openInputStream(); + try { + byte buffer[] = new byte[20000]; + int size = in.read(buffer); + while (size > 0) { + out.write(buffer, 0, size); + size = in.read(buffer); + } + } + finally { + in.close(); + } + } + + static void traverse(Jar jar, int rootlength, File directory, + Pattern doNotCopy) { + if (doNotCopy.matcher(directory.getName()).matches()) + return; + + File files[] = directory.listFiles(); + for (int i = 0; i < files.length; i++) { + if (files[i].isDirectory()) + traverse(jar, rootlength, files[i], doNotCopy); + else { + String path = files[i].getAbsolutePath().substring( + rootlength + 1); + if (File.separatorChar != '/') + path = path.replace(File.separatorChar, '/'); + jar.putResource(path, new FileResource(files[i]), true); + } + } + } + + public long lastModified() { + return file.lastModified(); + } + + public String getExtra() { + return extra; + } + + public void setExtra(String extra) { + this.extra = extra; + } + + public long size() { + return (int) file.length(); + } +} Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/FileResource.java ------------------------------------------------------------------------------ svn:eol-style = native Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Instruction.java URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Instruction.java?rev=793527&view=auto ============================================================================== --- felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Instruction.java (added) +++ felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Instruction.java Mon Jul 13 10:06:47 2009 @@ -0,0 +1,113 @@ +/* + * $Header: /cvsroot/bnd/aQute.bnd/src/aQute/lib/osgi/Instruction.java,v 1.1 2009/01/19 14:08:30 pkriens Exp $ + * + * Copyright (c) OSGi Alliance (2006). All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aQute.lib.osgi; + +import java.util.regex.*; + +public class Instruction { + Pattern pattern; + String instruction; + boolean negated; + boolean optional; + + public Instruction(String instruction, boolean negated) { + this.instruction = instruction; + this.negated = negated; + } + + public boolean matches(String value) { + return getMatcher(value).matches(); + } + + public boolean isNegated() { + return negated; + } + + public String getPattern() { + return instruction; + } + + /** + * Convert a string based pattern to a regular expression based pattern. + * This is called an instruction, this object makes it easier to handle the + * different cases + * + * @param string + * @return + */ + public static Instruction getPattern(String string) { + boolean negated = false; + if (string.startsWith("!")) { + negated = true; + string = string.substring(1); + } + StringBuffer sb = new StringBuffer(); + for (int c = 0; c < string.length(); c++) { + switch (string.charAt(c)) { + case '.': + sb.append("\\."); + break; + case '*': + sb.append(".*"); + break; + case '?': + sb.append(".?"); + break; + default: + sb.append(string.charAt(c)); + break; + } + } + string = sb.toString(); + if (string.endsWith("\\..*")) { + sb.append("|"); + sb.append(string.substring(0, string.length() - 4)); + } + return new Instruction(sb.toString(), negated); + } + + public String toString() { + return getPattern(); + } + + public Matcher getMatcher(String value) { + if (pattern == null) { + pattern = Pattern.compile(instruction); + } + return pattern.matcher(value); + } + + public int hashCode() { + return instruction.hashCode(); + } + + public boolean equals(Object other) { + return other != null && (other instanceof Instruction) + && instruction.equals(((Instruction) other).instruction); + } + + public void setOptional() { + optional = true; + } + + public boolean isOptional() { + return optional; + } + +} Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Instruction.java ------------------------------------------------------------------------------ svn:eol-style = native Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/InstructionFilter.java URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/InstructionFilter.java?rev=793527&view=auto ============================================================================== --- felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/InstructionFilter.java (added) +++ felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/InstructionFilter.java Mon Jul 13 10:06:47 2009 @@ -0,0 +1,34 @@ +/* Copyright 2009 aQute SARL + * Licensed under the Apache License, Version 2.0, see http://www.apache.org/licenses/LICENSE-2.0 */ +package aQute.lib.osgi; + +import java.io.File; +import java.io.FileFilter; + +public class InstructionFilter implements FileFilter { + + private Instruction instruction; + private boolean recursive; + + public InstructionFilter (Instruction instruction, boolean recursive) { + this.instruction = instruction; + this.recursive = recursive; + } + public boolean isRecursive() { + return recursive; + } + public boolean accept(File pathname) { + if (Analyzer.doNotCopy.matcher(pathname.getName()).matches()) { + return false; + } + + if (pathname.isDirectory() && isRecursive()) { + return true; + } + + if (instruction == null) { + return true; + } + return !instruction.isNegated() & instruction.matches(pathname.getName()); + } +} Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/InstructionFilter.java ------------------------------------------------------------------------------ svn:eol-style = native Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Jar.java URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Jar.java?rev=793527&view=auto ============================================================================== --- felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Jar.java (added) +++ felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Jar.java Mon Jul 13 10:06:47 2009 @@ -0,0 +1,429 @@ +/* Copyright 2006 aQute SARL + * Licensed under the Apache License, Version 2.0, see http://www.apache.org/licenses/LICENSE-2.0 */ +package aQute.lib.osgi; + +import java.io.*; +import java.util.*; +import java.util.jar.*; +import java.util.regex.*; +import java.util.zip.*; + +import aQute.libg.reporter.*; + +public class Jar implements Closeable { + public static final Object[] EMPTY_ARRAY = new Jar[0]; + Map resources = new TreeMap(); + Map> directories = new TreeMap>(); + Manifest manifest; + boolean manifestFirst; + String name; + File source; + ZipFile zipFile; + long lastModified; + String lastModifiedReason; + Reporter reporter; + boolean doNotTouchManifest; + boolean nomanifest; + + public Jar(String name) { + this.name = name; + } + + public Jar(String name, File dirOrFile) throws ZipException, IOException { + this(name); + source = dirOrFile; + if (dirOrFile.isDirectory()) + FileResource.build(this, dirOrFile, Analyzer.doNotCopy); + else { + zipFile = ZipResource.build(this, dirOrFile); + } + } + + public Jar(String name, InputStream in, long lastModified) + throws IOException { + this(name); + EmbeddedResource.build(this, in, lastModified); + } + + public Jar(String name, String path) throws IOException { + this(name); + File f = new File(path); + InputStream in = new FileInputStream(f); + EmbeddedResource.build(this, in, f.lastModified()); + in.close(); + } + + public Jar(File jar) throws IOException { + this(getName(jar), jar); + } + + /** + * Make the JAR file name the project name if we get a src or bin directory. + * + * @param f + * @return + */ + private static String getName(File f) { + f = f.getAbsoluteFile(); + String name = f.getName(); + if (name.equals("bin") || name.equals("src")) + return f.getParentFile().getName(); + else { + if (name.endsWith(".jar")) + name = name.substring(0, name.length() - 4); + return name; + } + } + + public Jar(String string, InputStream resourceAsStream) throws IOException { + this(string, resourceAsStream, 0); + } + + public void setName(String name) { + this.name = name; + } + + public String toString() { + return "Jar:" + name; + } + + public boolean putResource(String path, Resource resource) { + return putResource(path, resource, true); + } + + public boolean putResource(String path, Resource resource, boolean overwrite) { + updateModified(resource.lastModified(), path); + + if (path.equals("META-INF/MANIFEST.MF")) { + manifest = null; + if (resources.isEmpty()) + manifestFirst = true; + } + String dir = getDirectory(path); + Map s = directories.get(dir); + if (s == null) { + s = new TreeMap(); + directories.put(dir, s); + int n = dir.lastIndexOf('/'); + while (n > 0) { + String dd = dir.substring(0, n); + if (directories.containsKey(dd)) + break; + directories.put(dd, null); + n = dd.lastIndexOf('/'); + } + } + boolean duplicate = s.containsKey(path); + if (!duplicate || overwrite) { + resources.put(path, resource); + s.put(path, resource); + } + return duplicate; + } + + public Resource getResource(String path) { + return resources.get(path); + } + + private String getDirectory(String path) { + int n = path.lastIndexOf('/'); + if (n < 0) + return ""; + + return path.substring(0, n); + } + + public Map> getDirectories() { + return directories; + } + + public Map getResources() { + return resources; + } + + public boolean addDirectory(Map directory, + boolean overwrite) { + boolean duplicates = false; + if (directory == null) + return false; + + for (Map.Entry entry : directory.entrySet()) { + String key = entry.getKey(); + if (!key.endsWith(".java")) { + duplicates |= putResource(key, (Resource) entry.getValue(), + overwrite); + } + } + return duplicates; + } + + public Manifest getManifest() throws IOException { + if (manifest == null) { + Resource manifestResource = getResource("META-INF/MANIFEST.MF"); + if (manifestResource != null) { + InputStream in = manifestResource.openInputStream(); + manifest = new Manifest(in); + in.close(); + } + } + return manifest; + } + + public boolean exists(String path) { + return resources.containsKey(path); + } + + public void setManifest(Manifest manifest) { + manifestFirst = true; + this.manifest = manifest; + } + + public void write(File file) throws Exception { + try { + OutputStream out = new FileOutputStream(file); + write(out); + out.close(); + return; + + } catch (Exception t) { + file.delete(); + throw t; + } + } + + public void write(String file) throws Exception { + write(new File(file)); + } + + public void write(OutputStream out) throws IOException { + ZipOutputStream jout = nomanifest ? new ZipOutputStream(out) : new JarOutputStream(out); + Set done = new HashSet(); + + Set directories = new HashSet(); + if (doNotTouchManifest) { + writeResource(jout, directories, "META-INF/MANIFEST.MF", + getResource("META-INF/MANIFEST.MF")); + done.add("META-INF/MANIFEST.MF"); + } else if (!nomanifest) + doManifest(done, jout); + + for (Map.Entry entry : getResources().entrySet()) { + // Skip metainf contents + if (!done.contains(entry.getKey())) + writeResource(jout, directories, (String) entry.getKey(), + (Resource) entry.getValue()); + } + jout.finish(); + } + + private void doManifest(Set done, ZipOutputStream jout) + throws IOException { + if ( nomanifest ) + return; + + JarEntry ze = new JarEntry("META-INF/MANIFEST.MF"); + jout.putNextEntry(ze); + writeManifest(jout); + jout.closeEntry(); + done.add(ze.getName()); + } + + /** + * Cleanup the manifest for writing. Cleaning up consists of adding a space + * after any \n to prevent the manifest to see this newline as a delimiter. + * + * @param out + * Output + * @throws IOException + */ + + public void writeManifest(OutputStream out) throws IOException { + writeManifest(getManifest(), out); + } + + public static void writeManifest(Manifest manifest, OutputStream out) + throws IOException { + + manifest = clean(manifest); + manifest.write(out); + } + + private static Manifest clean(Manifest org) { + + Manifest result = new Manifest(); + for (Map.Entry entry : org.getMainAttributes().entrySet()) { + String nice = clean((String) entry.getValue()); + result.getMainAttributes().put(entry.getKey(), nice); + } + for (String name : org.getEntries().keySet()) { + Attributes attrs = result.getAttributes(name); + if (attrs == null) { + attrs = new Attributes(); + result.getEntries().put(name, attrs); + } + + for (Map.Entry entry : org.getAttributes(name).entrySet()) { + String nice = clean((String) entry.getValue()); + attrs.put((Attributes.Name) entry.getKey(), nice); + } + } + return result; + } + + private static String clean(String s) { + if (s.indexOf('\n') < 0) + return s; + + StringBuffer sb = new StringBuffer(s); + for (int i = 0; i < sb.length(); i++) { + if (sb.charAt(i) == '\n') + sb.insert(++i, ' '); + } + return sb.toString(); + } + + private void writeResource(ZipOutputStream jout, Set directories, + String path, Resource resource) throws IOException { + if (resource == null) + return; + + createDirectories(directories, jout, path); + ZipEntry ze = new ZipEntry(path); + ze.setMethod(ZipEntry.DEFLATED); + long lastModified = resource.lastModified(); + if (lastModified == 0L) { + lastModified = System.currentTimeMillis(); + } + ze.setTime(lastModified); + if (resource.getExtra() != null) + ze.setExtra(resource.getExtra().getBytes()); + jout.putNextEntry(ze); + try { + resource.write(jout); + } catch (Exception e) { + throw new IllegalArgumentException("Cannot write resource: " + path + + " " + e); + } + jout.closeEntry(); + } + + void createDirectories(Set directories, ZipOutputStream zip, + String name) throws IOException { + int index = name.lastIndexOf('/'); + if (index > 0) { + String path = name.substring(0, index); + if (directories.contains(path)) + return; + createDirectories(directories, zip, path); + ZipEntry ze = new ZipEntry(path + '/'); + zip.putNextEntry(ze); + zip.closeEntry(); + directories.add(path); + } + } + + public String getName() { + return name; + } + + /** + * Add all the resources in the given jar that match the given filter. + * + * @param sub + * the jar + * @param filter + * a pattern that should match the resoures in sub to be added + */ + public boolean addAll(Jar sub, Pattern filter) { + boolean dupl = false; + for (String name : sub.getResources().keySet()) { + if ("META-INF/MANIFEST.MF".equals(name)) + continue; + + if (filter == null || filter.matcher(name).matches()) + dupl |= putResource(name, sub.getResource(name), true); + } + return dupl; + } + + public void close() { + if (zipFile != null) + try { + zipFile.close(); + } catch (IOException e) { + // Ignore + } + resources = null; + directories = null; + manifest = null; + source = null; + } + + public long lastModified() { + return lastModified; + } + + public void updateModified(long time, String reason) { + if (time > lastModified) { + lastModified = time; + lastModifiedReason = reason; + } + } + + public void setReporter(Reporter reporter) { + this.reporter = reporter; + } + + public boolean hasDirectory(String path) { + return directories.get(path) != null; + } + + public List getPackages() { + List list = new ArrayList(directories.size()); + + for (Iterator i = directories.keySet().iterator(); i.hasNext();) { + String path = i.next(); + String pack = path.replace('/', '.'); + list.add(pack); + } + return list; + } + + public File getSource() { + return source; + } + + public boolean addAll(Jar src) { + return addAll(src, null); + } + + public boolean rename(String oldPath, String newPath) { + Resource resource = remove(oldPath); + if (resource == null) + return false; + + return putResource(newPath, resource); + } + + public Resource remove(String path) { + Resource resource = resources.remove(path); + String dir = getDirectory(path); + Map mdir = directories.get(dir); + // must be != null + mdir.remove(path); + return resource; + } + + /** + * Make sure nobody touches the manifest! If the bundle is signed, we do not + * want anybody to touch the manifest after the digests have been + * calculated. + */ + public void setDoNotTouchManifest() { + doNotTouchManifest = true; + } + + public void setNoManifest(boolean b) { + nomanifest = b; + } +} Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Jar.java ------------------------------------------------------------------------------ svn:eol-style = native Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/JarResource.java URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/JarResource.java?rev=793527&view=auto ============================================================================== --- felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/JarResource.java (added) +++ felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/JarResource.java Mon Jul 13 10:06:47 2009 @@ -0,0 +1,42 @@ +package aQute.lib.osgi; + +import java.io.*; + +public class JarResource implements Resource { + Jar jar; + String extra; + + public JarResource(Jar jar ) { + this.jar = jar; + } + + public String getExtra() { + return extra; + } + + public long lastModified() { + return jar.lastModified(); + } + + + public void write(OutputStream out) throws IOException { + jar.write(out); + } + + public InputStream openInputStream() throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + write(out); + out.close(); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + return in; + } + + public void setExtra(String extra) { + this.extra = extra; + } + + public Jar getJar() { + return jar; + } + +} Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/JarResource.java ------------------------------------------------------------------------------ svn:eol-style = native Added: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Macro.java URL: http://svn.apache.org/viewvc/felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Macro.java?rev=793527&view=auto ============================================================================== --- felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Macro.java (added) +++ felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Macro.java Mon Jul 13 10:06:47 2009 @@ -0,0 +1,841 @@ +/* Copyright 2006 aQute SARL + * Licensed under the Apache License, Version 2.0, see http://www.apache.org/licenses/LICENSE-2.0 */ +package aQute.lib.osgi; + +import java.io.*; +import java.lang.reflect.*; +import java.net.*; +import java.text.*; +import java.util.*; +import java.util.regex.*; + +import aQute.libg.sed.*; +import aQute.libg.version.*; + +/** + * Provide a macro processor. This processor can replace variables in strings + * based on a properties and a domain. The domain can implement functions that + * start with a "_" and take args[], the names of these functions are available + * as functions in the macro processor (without the _). Macros can nest to any + * depth but may not contain loops. + * + */ +public class Macro implements Replacer { + Properties properties; + Processor domain; + Object targets[]; + boolean flattening; + + public Macro(Properties properties, Processor domain, Object... targets) { + this.properties = properties; + this.domain = domain; + this.targets = targets; + if (targets != null) { + for (Object o : targets) { + assert o != null; + } + } + } + + public Macro(Processor processor) { + this(new Properties(), processor); + } + + public String process(String line) { + return process(line, null); + } + + String process(String line, Link link) { + StringBuffer sb = new StringBuffer(); + process(line, 0, '\u0000', '\u0000', sb, link); + return sb.toString(); + } + + int process(CharSequence org, int index, char begin, char end, + StringBuffer result, Link link) { + StringBuilder line = new StringBuilder(org); + int nesting = 1; + + StringBuffer variable = new StringBuffer(); + outer: while (index < line.length()) { + char c1 = line.charAt(index++); + if (c1 == end) { + if (--nesting == 0) { + result.append(replace(variable.toString(), link)); + return index; + } + } else if (c1 == begin) + nesting++; + else if (c1 == '\\' && index < line.length() - 1 + && line.charAt(index) == '$') { + // remove the escape backslash and interpret the dollar as a + // literal + index++; + variable.append('$'); + continue outer; + } else if (c1 == '$' && index < line.length() - 2) { + char c2 = line.charAt(index); + char terminator = getTerminator(c2); + if (terminator != 0) { + index = process(line, index + 1, c2, terminator, variable, + link); + continue outer; + } + } + variable.append(c1); + } + result.append(variable); + return index; + } + + public static char getTerminator(char c) { + switch (c) { + case '(': + return ')'; + case '[': + return ']'; + case '{': + return '}'; + case '<': + return '>'; + case '\u00ab': // Guillemet double << >> + return '\u00bb'; + case '\u2039': // Guillemet single + return '\u203a'; + } + return 0; + } + + protected String replace(String key, Link link) { + if (link != null && link.contains(key)) + return "${infinite:" + link.toString() + "}"; + + if (key != null) { + key = key.trim(); + if (key.length() > 0) { + String value = (String) properties.getProperty(key); + if (value != null) + return process(value, new Link(link, key)); + + value = doCommands(key); + if (value != null) + return process(value, new Link(link, key)); + + if (key != null && key.trim().length() > 0) { + value = System.getProperty(key); + if (value != null) + return value; + } + if (!flattening) + domain.warning("No translation found for macro: " + key); + } else { + domain.warning("Found empty macro key"); + } + } else { + domain.warning("Found null macro key"); + } + return "${" + key + "}"; + } + + /** + * Parse the key as a command. A command consist of parameters separated by + * ':'. + * + * @param key + * @return + */ + static Pattern commands = Pattern.compile("(?= 0) + args[i] = args[i].replaceAll("\\\\;", ";"); + + Processor rover = domain; + while (rover != null) { + String result = doCommand(rover, args[0], args); + if (result != null) + return result; + + rover = rover.getParent(); + } + + for (int i = 0; targets != null && i < targets.length; i++) { + String result = doCommand(targets[i], args[0], args); + if (result != null) + return result; + } + + return doCommand(this, args[0], args); + } + + private String doCommand(Object target, String method, String[] args) { + if (target == null) + ; // System.out.println("Huh? Target should never be null " + + // domain); + else { + String cname = "_" + method.replaceAll("-", "_"); + try { + Method m = target.getClass().getMethod(cname, + new Class[] { String[].class }); + return (String) m.invoke(target, new Object[] { args }); + } catch (NoSuchMethodException e) { + // Ignore + } catch (InvocationTargetException e) { + domain.warning("Exception in replace: " + e.getCause()); + e.printStackTrace(); + } catch (Exception e) { + domain.warning("Exception in replace: " + e + " method=" + + method); + e.printStackTrace(); + } + } + return null; + } + + /** + * Return a unique list where the duplicates are removed. + * + * @param args + * @return + */ + static String _uniqHelp = "${uniq; ...}"; + + public String _uniq(String args[]) { + verifyCommand(args, _uniqHelp, null, 1, Integer.MAX_VALUE); + Set set = new LinkedHashSet(); + for (int i = 1; i < args.length; i++) { + Processor.split(args[i], set); + } + return Processor.join(set, ","); + } + + public String _filter(String args[]) { + return filter(args, false); + } + + public String _filterout(String args[]) { + return filter(args, true); + + } + + static String _filterHelp = "${%s;;}"; + + String filter(String[] args, boolean include) { + verifyCommand(args, String.format(_filterHelp, args[0]), null, 3, 3); + + Collection list = new ArrayList(Processor + .split(args[1])); + Pattern pattern = Pattern.compile(args[2]); + + for (Iterator i = list.iterator(); i.hasNext();) { + if (pattern.matcher(i.next()).matches() == include) + i.remove(); + } + return Processor.join(list); + } + + static String _sortHelp = "${sort;...}"; + + public String _sort(String args[]) { + verifyCommand(args, _sortHelp, null, 2, Integer.MAX_VALUE); + + List result = new ArrayList(); + for (int i = 1; i < args.length; i++) { + Processor.split(args[i], result); + } + Collections.sort(result); + return Processor.join(result); + } + + static String _joinHelp = "${join;...}"; + + public String _join(String args[]) { + + verifyCommand(args, _joinHelp, null, 1, Integer.MAX_VALUE); + + List result = new ArrayList(); + for (int i = 1; i < args.length; i++) { + Processor.split(args[i], result); + } + return Processor.join(result); + } + + static String _ifHelp = "${if;; [;] }"; + + public String _if(String args[]) { + verifyCommand(args, _ifHelp, null, 3, 4); + String condition = args[1].trim(); + if (condition.length() != 0) + return args[2]; + if (args.length > 3) + return args[3]; + else + return ""; + } + + public String _now(String args[]) { + return new Date().toString(); + } + + public static String _fmodifiedHelp = "${fmodified;...}, return latest modification date"; + + public String _fmodified(String args[]) throws Exception { + verifyCommand(args, _fmodifiedHelp, null, 2, Integer.MAX_VALUE); + + long time = 0; + Collection names = new ArrayList(); + for (int i = 1; i < args.length; i++) { + Processor.split(args[i], names); + } + for (String name : names) { + File f = new File(name); + if (f.exists() && f.lastModified() > time) + time = f.lastModified(); + } + return "" + time; + } + + public String _long2date(String args[]) { + try { + return new Date(Long.parseLong(args[1])).toString(); + } catch (Exception e) { + e.printStackTrace(); + } + return "not a valid long"; + } + + public String _literal(String args[]) { + if (args.length != 2) + throw new RuntimeException( + "Need a value for the ${literal;} macro"); + return "${" + args[1] + "}"; + } + + public String _def(String args[]) { + if (args.length != 2) + throw new RuntimeException( + "Need a value for the ${def;} macro"); + + String value = properties.getProperty(args[1]); + if (value == null) + return ""; + else + return value; + } + + /** + * + * replace ; ; regex ; replace + * + * @param args + * @return + */ + public String _replace(String args[]) { + if (args.length != 4) { + domain.warning("Invalid nr of arguments to replace " + + Arrays.asList(args)); + return null; + } + + String list[] = args[1].split("\\s*,\\s*"); + StringBuffer sb = new StringBuffer(); + String del = ""; + for (int i = 0; i < list.length; i++) { + String element = list[i].trim(); + if (!element.equals("")) { + sb.append(del); + sb.append(element.replaceAll(args[2], args[3])); + del = ", "; + } + } + + return sb.toString(); + } + + public String _warning(String args[]) { + for (int i = 1; i < args.length; i++) { + domain.warning(process(args[i])); + } + return ""; + } + + public String _error(String args[]) { + for (int i = 1; i < args.length; i++) { + domain.error(process(args[i])); + } + return ""; + } + + /** + * toclassname ; .class ( , .class ) * + * + * @param args + * @return + */ + static String _toclassnameHelp = "${classname;}, convert class paths to FQN class names "; + + public String _toclassname(String args[]) { + verifyCommand(args, _toclassnameHelp, null, 2, 2); + Collection paths = Processor.split(args[1]); + + List names = new ArrayList(paths.size()); + for (String path : paths) { + if (path.endsWith(".class")) { + String name = path.substring(0, path.length() - 6).replace('/', + '.'); + names.add(name); + } else if (path.endsWith(".java")) { + String name = path.substring(0, path.length() - 5).replace('/', + '.'); + names.add(name); + } else { + domain + .warning("in toclassname, " + + args[1] + + " is not a class path because it does not end in .class"); + } + } + return Processor.join(names, ","); + } + + /** + * toclassname ; .class ( , .class ) * + * + * @param args + * @return + */ + + static String _toclasspathHelp = "${toclasspath;[;boolean]}, convert a list of class names to paths"; + + public String _toclasspath(String args[]) { + verifyCommand(args, _toclasspathHelp, null, 2, 3); + boolean cl= true; + if (args.length>2) + cl = new Boolean(args[2]); + + Collection names = Processor.split(args[1]); + Collection paths = new ArrayList(names.size()); + for (String name : names) { + String path = name.replace('.', '/') + (cl ? ".class" : ""); + paths.add(path); + } + return Processor.join(paths, ","); + } + + public String _dir(String args[]) { + if (args.length < 2) { + domain.warning("Need at least one file name for ${dir;...}"); + return null; + } else { + String del = ""; + StringBuffer sb = new StringBuffer(); + for (int i = 1; i < args.length; i++) { + File f = new File(args[i]).getAbsoluteFile(); + if (f.exists() && f.getParentFile().exists()) { + sb.append(del); + sb.append(f.getParentFile().getAbsolutePath()); + del = ","; + } + } + return sb.toString(); + } + + } + + public String _basename(String args[]) { + if (args.length < 2) { + domain.warning("Need at least one file name for ${basename;...}"); + return null; + } else { + String del = ""; + StringBuffer sb = new StringBuffer(); + for (int i = 1; i < args.length; i++) { + File f = new File(args[i]).getAbsoluteFile(); + if (f.exists() && f.getParentFile().exists()) { + sb.append(del); + sb.append(f.getName()); + del = ","; + } + } + return sb.toString(); + } + + } + + public String _isfile(String args[]) { + if (args.length < 2) { + domain.warning("Need at least one file name for ${isfile;...}"); + return null; + } else { + boolean isfile = true; + for (int i = 1; i < args.length; i++) { + File f = new File(args[i]).getAbsoluteFile(); + isfile &= f.isFile(); + } + return isfile ? "true" : "false"; + } + + } + + public String _isdir(String args[]) { + if (args.length < 2) { + domain.warning("Need at least one file name for ${isdir;...}"); + return null; + } else { + boolean isdir = true; + for (int i = 1; i < args.length; i++) { + File f = new File(args[i]).getAbsoluteFile(); + isdir &= f.isDirectory(); + } + return isdir ? "true" : "false"; + } + + } + + public String _tstamp(String args[]) { + String format = "yyyyMMddHHmm"; + long now = System.currentTimeMillis(); + + if (args.length > 1) { + format = args[1]; + if (args.length > 2) { + now = Long.parseLong(args[2]); + if (args.length > 3) { + domain.warning("Too many arguments for tstamp: " + + Arrays.toString(args)); + } + } + } + SimpleDateFormat sdf = new SimpleDateFormat(format); + return sdf.format(new Date(now)); + } + + /** + * Wildcard a directory. The lists can contain Instruction that are matched + * against the given directory + * + * ${wc;;(;)*} + * + * @author aqute + * + */ + + public String _lsr(String args[]) { + return ls(args, true); + } + + public String _lsa(String args[]) { + return ls(args, false); + } + + String ls(String args[], boolean relative) { + if (args.length < 2) + throw new IllegalArgumentException( + "the ${ls} macro must at least have a directory as parameter"); + + File dir = new File(args[1]); + if (!dir.isAbsolute()) + throw new IllegalArgumentException( + "the ${ls} macro directory parameter is not absolute: " + + dir); + + if (!dir.exists()) + throw new IllegalArgumentException( + "the ${ls} macro directory parameter does not exist: " + + dir); + + if (!dir.isDirectory()) + throw new IllegalArgumentException( + "the ${ls} macro directory parameter points to a file instead of a directory: " + + dir); + + String[] files = dir.list(); + List result; + + if (args.length < 3) { + result = Arrays.asList(files); + } else + result = new ArrayList(); + + for (int i = 2; i < args.length; i++) { + String parts[] = args[i].split("\\s*,\\s*"); + for (String pattern : parts) { + // So make it in to an instruction + Instruction instr = Instruction.getPattern(pattern); + + // For each project, match it against the instruction + for (int f = 0; f < files.length; f++) { + if (files[f] != null) { + if (instr.matches(files[f])) { + if (!instr.isNegated()) { + if (relative) + result.add(files[f]); + else + result.add(new File(dir, files[f]) + .getAbsolutePath()); + } + files[f] = null; + } + } + } + } + } + return Processor.join(result, ","); + } + + public String _currenttime(String args[]) { + return Long.toString(System.currentTimeMillis()); + } + + /** + * Modify a version to set a version policy. Thed policy is a mask that is + * mapped to a version. + * + *
+     * +           increment
+     * -           decrement
+     * =           maintain
+     * ˜           discard
+     * 
+     * ==+      = maintain major, minor, increment micro, discard qualifier
+     * ˜˜˜=     = just get the qualifier
+     * version="[${version;==;${@}},${version;=+;${@}})"
+     * 
+ * + * + * + * + * @param args + * @return + */ + static String _versionHelp = "${version;;}, modify a version\n" + + " ::= [ M [ M [ M [ MQ ]]]\n" + + "M ::= '+' | '-' | MQ\n" + + "MQ ::= '~' | '='"; + static Pattern _versionPattern[] = new Pattern[] { null, null, + Pattern.compile("[-+=~]{0,3}[=~]?"), Verifier.VERSION }; + + public String _version(String args[]) { + verifyCommand(args, _versionHelp, null, 3, 3); + + String mask = args[1]; + + Version version = new Version(args[2]); + StringBuilder sb = new StringBuilder(); + String del = ""; + + for (int i = 0; i < mask.length(); i++) { + char c = mask.charAt(i); + String result = null; + if (c != '~') { + if (i == 3) { + result = version.getQualifier(); + } else if (Character.isDigit(c)) { + // Handle masks like +00, =+0 + result = String.valueOf(c); + } else { + int x = version.get(i); + switch (c) { + case '+': + x++; + break; + case '-': + x--; + break; + case '=': + break; + } + result = Integer.toString(x); + } + if (result != null) { + sb.append(del); + del = "."; + sb.append(result); + } + } + } + return sb.toString(); + } + + /** + * System command. Execute a command and insert the result. + * + * @param args + * @param help + * @param patterns + * @param low + * @param high + */ + public String _system(String args[]) throws Exception { + verifyCommand(args, + "${system;[;]}, execute a system command", null, + 2, 3); + String command = args[1]; + String input = null; + + if (args.length > 2) { + input = args[2]; + } + + Process process = Runtime.getRuntime().exec(command, null, + domain.getBase()); + if (input != null) { + process.getOutputStream().write(input.getBytes("UTF-8")); + } + process.getOutputStream().close(); + + String s = getString(process.getInputStream()); + process.getInputStream().close(); + int exitValue = process.waitFor(); + if (exitValue != 0) { + domain.error("System command " + command + " failed with " + + exitValue); + } + return s.trim(); + } + + /** + * Get the contents of a file. + * + * @param in + * @return + * @throws IOException + */ + + public String _cat(String args[]) throws IOException { + verifyCommand(args, "${cat;}, get the content of a file", null, 2, + 2); + File f = domain.getFile(args[1]); + if (f.isFile()) { + InputStream in = new FileInputStream(f); + return getString(in); + } else if (f.isDirectory()) { + return Arrays.toString(f.list()); + } else { + try { + URL url = new URL(args[1]); + InputStream in = url.openStream(); + return getString(in); + } catch (MalformedURLException mfue) { + // Ignore here + } + return null; + } + } + + public static String getString(InputStream in) throws IOException { + try { + StringBuilder sb = new StringBuilder(); + BufferedReader rdr = new BufferedReader(new InputStreamReader(in)); + String line = null; + while ((line = rdr.readLine()) != null) { + sb.append(line); + sb.append("\n"); + } + return sb.toString(); + } finally { + in.close(); + } + } + + public static void verifyCommand(String args[], String help, + Pattern[] patterns, int low, int high) { + String message = ""; + if (args.length > high) { + message = "too many arguments"; + } else if (args.length < low) { + message = "too few arguments"; + } else { + for (int i = 0; patterns != null && i < patterns.length + && i < args.length - 1; i++) { + if (patterns[i] != null + && !patterns[i].matcher(args[i + 1]).matches()) { + message += String.format( + "Argument %s (%s) does not match %s\n", i, args[i], + patterns[i].pattern()); + } + } + } + if (message.length() != 0) { + StringBuilder sb = new StringBuilder(); + String del = "${"; + for (String arg : args) { + sb.append(del); + sb.append(arg); + del = ";"; + } + sb.append("}, is not understood. "); + sb.append(message); + throw new IllegalArgumentException(sb.toString()); + } + } + + // Helper class to track expansion of variables + // on the stack. + static class Link { + Link previous; + String key; + + public Link(Link previous, String key) { + this.previous = previous; + this.key = key; + } + + public boolean contains(String key) { + if (this.key.equals(key)) + return true; + + if (previous == null) + return false; + + return previous.contains(key); + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + String del = "["; + for (Link r = this; r != null; r = r.previous) { + sb.append(del); + sb.append(r.key); + del = ","; + } + sb.append("]"); + return sb.toString(); + } + } + + /** + * Take all the properties and translate them to actual values. This method + * takes the set properties and traverse them over all entries, including + * the default properties for that properties. The values no longer contain + * macros. + * + * @return A new Properties with the flattened values + */ + public Properties getFlattenedProperties() { + // Some macros only work in a lower processor, so we + // do not report unknown macros while flattening + flattening = true; + try { + Properties flattened = new Properties(); + for (Enumeration e = properties.propertyNames(); e + .hasMoreElements();) { + String key = (String) e.nextElement(); + if (!key.startsWith("_")) + if ( key.startsWith("-")) + flattened.put(key, properties.getProperty(key)); + else + flattened.put(key, process(properties.getProperty(key))); + } + return flattened; + } finally { + flattening = false; + } + }; + +} Propchange: felix/trunk/bundleplugin/src/main/java/aQute/lib/osgi/Macro.java ------------------------------------------------------------------------------ svn:eol-style = native