You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ds...@apache.org on 2009/07/21 20:51:41 UTC

svn commit: r796467 [25/25] - in /felix/trunk/sigil: common/core.tests/src/org/apache/felix/sigil/core/ common/core/src/org/apache/felix/sigil/bnd/ common/core/src/org/apache/felix/sigil/config/ common/core/src/org/apache/felix/sigil/core/ common/core/...

Modified: felix/trunk/sigil/ivy/resolver/src/org/apache/felix/sigil/ivy/SigilParser.java
URL: http://svn.apache.org/viewvc/felix/trunk/sigil/ivy/resolver/src/org/apache/felix/sigil/ivy/SigilParser.java?rev=796467&r1=796466&r2=796467&view=diff
==============================================================================
--- felix/trunk/sigil/ivy/resolver/src/org/apache/felix/sigil/ivy/SigilParser.java (original)
+++ felix/trunk/sigil/ivy/resolver/src/org/apache/felix/sigil/ivy/SigilParser.java Tue Jul 21 18:51:33 2009
@@ -19,6 +19,7 @@
 
 package org.apache.felix.sigil.ivy;
 
+
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -60,518 +61,636 @@
 import org.apache.ivy.plugins.repository.url.URLResource;
 import org.apache.ivy.plugins.resolver.DependencyResolver;
 
-public class SigilParser implements ModuleDescriptorParser {
 
-	private static DelegateParser instance;
+public class SigilParser implements ModuleDescriptorParser
+{
+
+    private static DelegateParser instance;
+
 
-	// used by ProjectRepository
-    static DelegateParser instance() {
-		if (instance == null)
-			throw new IllegalStateException("SigilParser is not instantiated.");
-		return instance;
-	}
-
-	public SigilParser() {
-		if (instance == null) {
-			instance = new DelegateParser();
-		}
-	}
-	
-	/**
-	 * In IvyDE, IvyContext is not available, so we can't find the SigilResolver.
-	 * This allows us to construct one.
-	 * @deprecated temporary to support IvyDE
-	 */
-	public void setConfig(String config) {
-		instance.config = config;
-	}
-
-	/**
-	 * sets delegate parser.
-	 * If not set, we delegate to the default Ivy parser.
-	 * @param type name returned by desired parser's getType() method.
-	 */
-	public void setDelegateType(String type) {
-		for (ModuleDescriptorParser parser : ModuleDescriptorParserRegistry.getInstance().getParsers()) {
-			if (parser.getType().equals(type)) {
-				instance.delegate = parser;
-				break;
-			}
-		}
-
-		if (instance.delegate == null) {
-			throw new IllegalArgumentException("Can't find parser delegateType=" + type);
-		}
-	}
-
-	/**
-	 * sets default file name the delegate parser accepts.
-	 * If not set, defaults to "ivy.xml".
-	 * @param name
-	 */
-	public void setDelegateFile(String name) {
-		instance.ivyFile = name;
-	}
-
-	/**
-	 * sets the dependencies to keep from the delegate parser.
-	 * If not set, all existing dependencies are dropped.
-	 * @param regex pattern matching dependency names to keep.
-	 */
-	public void setKeepDependencies(String regex) {
-		instance.keepDependencyPattern = Pattern.compile(regex);
-	}
-	
-	/**
-	 * reduce level of info logging.
-	 * @param quiet
-	 */
-	public void setQuiet(String quiet) {
-		instance.quiet = Boolean.parseBoolean(quiet);
-	}
-
-	/**
-	 * sets the SigilResolver we use.
-	 * If not set, we use the first SigilResolver we find.
-	 * @param name
-	 */
-	public void setResolver(String name) {
-		instance.resolverName = name;
-	}
-
-	/**
-	 * adds override element: <override name="X" pattern="Y" replace="Z"/>. Overrides
-	 * Ivy variables using a pattern substitution on the resource directory path.
-	 * 
-	 * @deprecated
-	 * This is only needed when a delegate parser expects Ant variables to be set
-	 * during the ivy:buildlist task (or the ProjectRepository initialisation),
-	 * which they are not. The delegate parser should really be fixed, as otherwise
-	 * the ivy:buildlist task won't work without this workaround.
-	 */
-	// e.g. <override name="ant.project.name" pattern=".*/([^/]+)/([^/]+)$" replace="$1-$2"/>
-	public void addConfiguredOverride(Map<String, String> var) {
-		final String name = var.get("name");
-		final String regex = var.get("pattern");
-		final String replace = var.get("replace");
-
-		if (name == null || regex == null || replace == null)
-			throw new IllegalArgumentException("override must contain name, pattern and replace attributes.");
-
-		instance.varRegex.put(name, Pattern.compile(regex));
-		instance.varReplace.put(name, replace);
-	}
-
-	// implement ModuleDescriptorParser interface by delegation to singleton instance.
-
-	public boolean accept(Resource res) {
-		return instance.accept(res);
-	}
-
-	public Artifact getMetadataArtifact(ModuleRevisionId mrid, Resource res) {
-		return instance.getMetadataArtifact(mrid, res);
-	}
-
-	public String getType() {
-		return instance.getType();
-	}
-
-	public ModuleDescriptor parseDescriptor(ParserSettings settings, URL xmlURL, boolean validate)
-			throws ParseException, IOException {
-		return instance.parseDescriptor(settings, xmlURL, validate);
-	}
-
-	public ModuleDescriptor parseDescriptor(ParserSettings settings, URL xmlURL, Resource res, boolean validate)
-			throws ParseException, IOException {
-		return instance.parseDescriptor(settings, xmlURL, res, validate);
-	}
-
-	public void toIvyFile(InputStream source, Resource res, File destFile, ModuleDescriptor md) throws ParseException,
-			IOException {
-		instance.toIvyFile(source, res, destFile, md);
-	}
-
-	/**
-	 * delegating parser.
-	 * (optionally) removes original dependencies and augments with sigil-determined dependencies.
-	 */
-	static class DelegateParser extends XmlModuleDescriptorParser {
-
-		private static final String SIGIL_BUILDLIST = IBldProject.PROJECT_FILE;
-		private static final String KEEP_ALL = ".*";
-		
-		private IBldResolver resolver;
-		private ParserSettings defaultSettings;
-		private Map<String, DefaultModuleDescriptor> rawCache = new HashMap<String, DefaultModuleDescriptor>();
-		private Map<String, DefaultModuleDescriptor> augmentedCache = new HashMap<String, DefaultModuleDescriptor>();
-
-		private HashMap<String, String> varReplace = new HashMap<String, String>();
-		private HashMap<String, Pattern> varRegex = new HashMap<String, Pattern>();
-
-		private ModuleDescriptorParser delegate;
-		private String ivyFile = "ivy.xml";
-		private String resolverName;
-		private Pattern keepDependencyPattern;
-    	private boolean quiet;
-    	private String config;
-
-		@Override
-		public boolean accept(Resource res) {
-			boolean accept = (res instanceof SigilResolver.SigilIvy)
-					|| res.getName().endsWith("/" + SIGIL_BUILDLIST)
-					|| ((instance.getSigilURI(res) != null) &&
-						((instance.delegate != null ? instance.delegate.accept(res) : false) || super.accept(res)));
-			if (accept)
-    			Log.verbose("accepted: " + res);
-			return accept;
-		}
-
-		@Override
-		public void toIvyFile(InputStream source, Resource res, File destFile, ModuleDescriptor md)
-				throws ParseException, IOException {
-			Log.verbose("writing resource: " + res + " toIvyFile: " + destFile);
-			// source allows us to keep layout and comments,
-			// but this doesn't work if source is not ivy.xml.
-			// So just write file directly from descriptor.
-			try {
-				XmlModuleDescriptorWriter.write(md, destFile);
-			} finally {
-				if (source != null)
-					source.close();
-			}
-		}
-		
-		@Override
-		public ModuleDescriptor parseDescriptor(ParserSettings settings, URL xmlURL, boolean validate)
-				throws ParseException, IOException {
-			return parseDescriptor(settings, xmlURL, new URLResource(xmlURL), validate);
-		}
-
-		@Override
-		public ModuleDescriptor parseDescriptor(ParserSettings settings, URL xmlURL, Resource res, boolean validate)
-				throws ParseException, IOException {
-			String cacheKey = xmlURL.toString() + settings.hashCode();
-			DefaultModuleDescriptor dmd = augmentedCache.get(cacheKey);
-
-			if (dmd == null) {
-				dmd = rawCache.get(cacheKey);
-				if (dmd == null) {
-					dmd = delegateParse(settings, xmlURL, res, validate);
-    			}
-
-				if (!quiet)
-    				Log.info("augmenting module=" + dmd.getModuleRevisionId().getName() +
-						" ant.project.name=" + settings.substitute("${ant.project.name}"));
-				
-				addDependenciesToDescriptor(res, dmd);
-				augmentedCache.put(cacheKey, dmd);
-				
-				Log.verbose("augmented dependencies: " + Arrays.asList(dmd.getDependencies()));
-			}
-
-			return dmd;
-		}
-		
-		// used by ProjectRepository
-		ModuleDescriptor parseDescriptor(URL projectURL) throws ParseException, IOException {
-			if (defaultSettings == null) {
-				Ivy ivy = IvyContext.getContext().peekIvy();
-				if (ivy == null)
-					throw new IllegalStateException("can't get default settings - no ivy context.");
-				defaultSettings = ivy.getSettings();
-			}
-			
-			URL ivyURL = new URL(projectURL, ivyFile);
-			String cacheKey = ivyURL.toString() + defaultSettings.hashCode();
-			DefaultModuleDescriptor dmd = rawCache.get(cacheKey);
-			
-			if (dmd == null) {
-				URLResource res = new URLResource(ivyURL);
-				// Note: this doesn't contain the augmented dependencies, which is OK,
-				// since the ProjectRepository only needs the id and status.
-				dmd = delegateParse(defaultSettings, ivyURL, res, false);
-				rawCache.put(cacheKey, dmd);
-			}
-
-			return dmd;
-		}
-
-		private DefaultModuleDescriptor delegateParse(ParserSettings settings, URL xmlURL, Resource res,
-				boolean validate) throws ParseException, IOException {
-			String resName = res.getName();
-
-			if (resName.endsWith("/" + SIGIL_BUILDLIST)) {
-				String name = resName.substring(0, resName.length() - SIGIL_BUILDLIST.length());
-				res = res.clone(name + ivyFile);
-				xmlURL = new URL(xmlURL, ivyFile);
-			}
-
-			if (settings instanceof IvySettings) {
-				IvySettings ivySettings = (IvySettings) settings;
-				String dir = new File(res.getName()).getParent();
-
-				for (String name : varRegex.keySet()) {
-					Pattern regex = varRegex.get(name);
-					String replace = varReplace.get(name);
-
-					String value = regex.matcher(dir).replaceAll(replace);
-
-					Log.debug("overriding variable " + name + "=" + value);
-					ivySettings.setVariable(name, value);
-				}
-			}
-
-			ModuleDescriptor md = null;
-			if (delegate == null || !delegate.accept(res)) {
-				md = super.parseDescriptor(settings, xmlURL, res, validate);
-			} else {
-				md = delegate.parseDescriptor(settings, xmlURL, res, validate);
-			}
-
-			return mungeDescriptor(md, res);
-		}
-
-		/**
-		 * clones descriptor and removes dependencies, as descriptor MUST have
-		 * 'this' as the parser given to its constructor.
-		 * Only dependency names matched by keepDependencyPattern are kept,
-		 * as we're going to add our own dependencies later.
-		 */
-		private DefaultModuleDescriptor mungeDescriptor(ModuleDescriptor md, Resource res) {
-
-			if ((md instanceof DefaultModuleDescriptor) &&
-					(md.getParser() == this) &&
-					(KEEP_ALL.equals(keepDependencyPattern))) {
-				return (DefaultModuleDescriptor) md;
-			}
-				
-			DefaultModuleDescriptor dmd = new DefaultModuleDescriptor(this, res);
-
-			dmd.setModuleRevisionId(md.getModuleRevisionId());
-			dmd.setPublicationDate(md.getPublicationDate());
-
-			for (Configuration c : md.getConfigurations()) {
-				String conf = c.getName();
-
-				dmd.addConfiguration(c);
-
-				for (Artifact a : md.getArtifacts(conf)) {
-					dmd.addArtifact(conf, a);
-				}
-			}
-
-			if (keepDependencyPattern != null) {
-    			for (DependencyDescriptor dependency : md.getDependencies()) {
-    				String name = dependency.getDependencyId().getName();
-    				if (keepDependencyPattern.matcher(name).matches()) {
-    					dmd.addDependency(dependency);
-    				}
-    			}
-			}
-
-			return dmd;
-		}
-
-		/*
-		 * find URI to Sigil project file. This assumes that it is in the same
-		 * directory as the ivy file.
-		 */
-		private URI getSigilURI(Resource res) {
-			URI uri = null;
-
-			if (res instanceof URLResource) {
-				URL url = ((URLResource) res).getURL();
-				uri = URI.create(url.toString()).resolve(IBldProject.PROJECT_FILE);
-				try {
-					InputStream stream = uri.toURL().openStream(); // check file
-																	// exists
-					stream.close();
-				} catch (IOException e) {
-					uri = null;
-				}
-			} else if (res instanceof FileResource) {
-				uri = ((FileResource) res).getFile().toURI().resolve(IBldProject.PROJECT_FILE);
-				if (!(new File(uri).exists()))
-					uri = null;
-			}
-
-			return uri;
-		}
-
-		/*
-		 * add sigil dependencies to ModuleDescriptor.
-		 */
-		private void addDependenciesToDescriptor(Resource res, DefaultModuleDescriptor md) throws IOException {
-			// FIXME: transitive should be configurable
-			final boolean transitive = true; // ivy default is true
-			final boolean changing = false;
-			final boolean force = false;
-
-			URI uri = getSigilURI(res);
-			if (uri == null)
-				return;
-
-			IBldProject project;
-
-			project = BldFactory.getProject(uri);
-
-			IBundleModelElement requirements = project.getDependencies();
-			Log.verbose("requirements: " + Arrays.asList(requirements.children()));
-
-			// preserve version range for Require-Bundle
-			// XXX: synthesise bundle version range corresponding to package version ranges?
-			HashMap<String, VersionRange> versions = new HashMap<String, VersionRange>();
-			for (IModelElement child : requirements.children()) {
-				if (child instanceof IRequiredBundle) {
-					IRequiredBundle bundle = (IRequiredBundle) child;
-					versions.put(bundle.getSymbolicName(), bundle.getVersions());
-				}
-			}
-
-			IBldResolver resolver = findResolver();
-			if (resolver == null) {
-				// this can happen in IvyDE, but it retries and is OK next time.
-				Log.warn("failed to find resolver - IvyContext not yet available.");
-				return;
-			}
-			
-			IResolution resolution = resolver.resolve(requirements, false);
-			Log.verbose("resolution: " + resolution.getBundles());
-
-			ModuleRevisionId masterMrid = md.getModuleRevisionId();
-			DefaultDependencyDescriptor dd;
-			ModuleRevisionId mrid;
-
-			for (ISigilBundle bundle : resolution.getBundles()) {
-				IBundleModelElement info = bundle.getBundleInfo();
-				String name = info.getSymbolicName();
-
-				if (name == null) {
-					// e.g. SystemProvider with framework=null
-					continue;
-				}
-
-				if (bundle instanceof ProjectRepository.ProjectBundle) {
-					ProjectRepository.ProjectBundle pb = (ProjectRepository.ProjectBundle) bundle;
-					String org = pb.getOrg();
-					if (org == null)
-						org = masterMrid.getOrganisation();
-					String id = pb.getId();
-					String module = pb.getModule();
-					String rev = pb.getRevision();
-
-					mrid = ModuleRevisionId.newInstance(org, module, rev);
-					dd = new SigilDependencyDescriptor(md, mrid, force, changing, transitive);
-
-					if (!id.equals(module)) { // non-default artifact
-						dd.addDependencyArtifact(module,
-								new DefaultDependencyArtifactDescriptor(dd, id, "jar", "jar", null, null));
-					}
-				} else {
-					VersionRange version = versions.get(name);
-					String rev = version != null ? version.toString() : info.getVersion().toString();
-					mrid = ModuleRevisionId.newInstance(SigilResolver.ORG_SIGIL, name, rev);
-					dd = new SigilDependencyDescriptor(md, mrid, force, changing, transitive);
-				}
-
-				int nDeps = 0;
-				boolean foundDefault = false;
-
-				// TODO: make dependency configurations configurable SIGIL-176
-				for (String conf : md.getConfigurationsNames()) {
-					if (conf.equals("default")) {
-						foundDefault = true;
-					} else if (md.getArtifacts(conf).length == 0) {
-						dd.addDependencyConfiguration(conf, conf + "(default)");
-						nDeps++;
-					}
-				}
-
-				if (nDeps > 0) {
-					if (foundDefault)
-						dd.addDependencyConfiguration("default", "default");
-				} else {
-					dd.addDependencyConfiguration("*", "*");    // all configurations
-				}
-
-				md.addDependency(dd);
-			}
-
-			boolean resolved = true;
-			for (IModelElement child : requirements.children()) {
-				ISigilBundle provider = resolution.getProvider(child);
-				if (provider == null) {
-					resolved = false;
-					// this is parse phase, so only log verbose message.
-					// error is produced during resolution phase.
-					Log.verbose("WARN: can't resolve: " + child);
-
-					String name;
-					String version;
-					if (child instanceof IRequiredBundle) {
-						IRequiredBundle rb = (IRequiredBundle) child;
-						name = rb.getSymbolicName();
-						version = rb.getVersions().toString();
-					} else {
-						IPackageImport pi = (IPackageImport) child;
-						name = "import!" + pi.getPackageName();
-						version = pi.getVersions().toString();
-					}
-
-					mrid = ModuleRevisionId.newInstance("!" + SigilResolver.ORG_SIGIL, name, version);
-					dd = new SigilDependencyDescriptor(md, mrid, force, changing, transitive);
-					dd.addDependencyConfiguration("*", "*"); // all
-					// configurations
-					md.addDependency(dd);
-				}
-			}
-
-			if (!resolved) {
-				// Ivy does not show warnings or errors logged from parse phase, until after resolution.
-				// but <buildlist> ant task doesn't do resolution, so errors would be silently ignored.
-				// Hence, we must use Message.info to ensure this failure is seen.
-				if (!quiet)
-    				Log.info("WARN: resolution failed in: " + masterMrid.getName() + ". Use -v for details.");
-			}
-		}
-
-		/*
-		 * find named resolver, or first resolver that implements IBldResolver.
-		 */
-		private IBldResolver findResolver() {
-			if (resolver == null) {
-				Ivy ivy = IvyContext.getContext().peekIvy();
-				if (ivy != null) {
-    				if (resolverName != null) {
-    					DependencyResolver r = ivy.getSettings().getResolver(resolverName);
-    					if (r == null) {
-    						throw new Error("SigilParser: resolver \"" + resolverName + "\" not defined.");
-    					} else if (r instanceof IBldResolver) {
-    						resolver = (IBldResolver) r;
-    					} else {
-    						throw new Error("SigilParser: resolver \"" + resolverName + "\" is not a SigilResolver.");
-    					}
-    				} else {
-    					for (Object r : ivy.getSettings().getResolvers()) {
-    						if (r instanceof IBldResolver) {
-    							resolver = (IBldResolver) r;
-    							break;
-    						}
-    					}
-    				}
-    
-    				if (resolver == null) {
-    					throw new Error("SigilParser: can't find SigilResolver. Check ivysettings.xml.");
-    				}
-				} else if (config != null) {
-					Log.warn("creating duplicate resolver to support IvyDE.");
-					resolver = new SigilResolver();
-					((SigilResolver)resolver).setConfig(config);
-				}
-			}
-			return resolver;
-		}
-	}
+    // used by ProjectRepository
+    static DelegateParser instance()
+    {
+        if ( instance == null )
+            throw new IllegalStateException( "SigilParser is not instantiated." );
+        return instance;
+    }
+
+
+    public SigilParser()
+    {
+        if ( instance == null )
+        {
+            instance = new DelegateParser();
+        }
+    }
+
+
+    /**
+     * In IvyDE, IvyContext is not available, so we can't find the SigilResolver.
+     * This allows us to construct one.
+     * @deprecated temporary to support IvyDE
+     */
+    public void setConfig( String config )
+    {
+        instance.config = config;
+    }
+
+
+    /**
+     * sets delegate parser.
+     * If not set, we delegate to the default Ivy parser.
+     * @param type name returned by desired parser's getType() method.
+     */
+    public void setDelegateType( String type )
+    {
+        for ( ModuleDescriptorParser parser : ModuleDescriptorParserRegistry.getInstance().getParsers() )
+        {
+            if ( parser.getType().equals( type ) )
+            {
+                instance.delegate = parser;
+                break;
+            }
+        }
+
+        if ( instance.delegate == null )
+        {
+            throw new IllegalArgumentException( "Can't find parser delegateType=" + type );
+        }
+    }
+
+
+    /**
+     * sets default file name the delegate parser accepts.
+     * If not set, defaults to "ivy.xml".
+     * @param name
+     */
+    public void setDelegateFile( String name )
+    {
+        instance.ivyFile = name;
+    }
+
+
+    /**
+     * sets the dependencies to keep from the delegate parser.
+     * If not set, all existing dependencies are dropped.
+     * @param regex pattern matching dependency names to keep.
+     */
+    public void setKeepDependencies( String regex )
+    {
+        instance.keepDependencyPattern = Pattern.compile( regex );
+    }
+
+
+    /**
+     * reduce level of info logging.
+     * @param quiet
+     */
+    public void setQuiet( String quiet )
+    {
+        instance.quiet = Boolean.parseBoolean( quiet );
+    }
+
+
+    /**
+     * sets the SigilResolver we use.
+     * If not set, we use the first SigilResolver we find.
+     * @param name
+     */
+    public void setResolver( String name )
+    {
+        instance.resolverName = name;
+    }
+
+
+    /**
+     * adds override element: <override name="X" pattern="Y" replace="Z"/>. Overrides
+     * Ivy variables using a pattern substitution on the resource directory path.
+     * 
+     * @deprecated
+     * This is only needed when a delegate parser expects Ant variables to be set
+     * during the ivy:buildlist task (or the ProjectRepository initialisation),
+     * which they are not. The delegate parser should really be fixed, as otherwise
+     * the ivy:buildlist task won't work without this workaround.
+     */
+    // e.g. <override name="ant.project.name" pattern=".*/([^/]+)/([^/]+)$" replace="$1-$2"/>
+    public void addConfiguredOverride( Map<String, String> var )
+    {
+        final String name = var.get( "name" );
+        final String regex = var.get( "pattern" );
+        final String replace = var.get( "replace" );
+
+        if ( name == null || regex == null || replace == null )
+            throw new IllegalArgumentException( "override must contain name, pattern and replace attributes." );
+
+        instance.varRegex.put( name, Pattern.compile( regex ) );
+        instance.varReplace.put( name, replace );
+    }
+
+
+    // implement ModuleDescriptorParser interface by delegation to singleton instance.
+
+    public boolean accept( Resource res )
+    {
+        return instance.accept( res );
+    }
+
+
+    public Artifact getMetadataArtifact( ModuleRevisionId mrid, Resource res )
+    {
+        return instance.getMetadataArtifact( mrid, res );
+    }
+
+
+    public String getType()
+    {
+        return instance.getType();
+    }
+
+
+    public ModuleDescriptor parseDescriptor( ParserSettings settings, URL xmlURL, boolean validate )
+        throws ParseException, IOException
+    {
+        return instance.parseDescriptor( settings, xmlURL, validate );
+    }
+
+
+    public ModuleDescriptor parseDescriptor( ParserSettings settings, URL xmlURL, Resource res, boolean validate )
+        throws ParseException, IOException
+    {
+        return instance.parseDescriptor( settings, xmlURL, res, validate );
+    }
+
+
+    public void toIvyFile( InputStream source, Resource res, File destFile, ModuleDescriptor md )
+        throws ParseException, IOException
+    {
+        instance.toIvyFile( source, res, destFile, md );
+    }
+
+    /**
+     * delegating parser.
+     * (optionally) removes original dependencies and augments with sigil-determined dependencies.
+     */
+    static class DelegateParser extends XmlModuleDescriptorParser
+    {
+
+        private static final String SIGIL_BUILDLIST = IBldProject.PROJECT_FILE;
+        private static final String KEEP_ALL = ".*";
+
+        private IBldResolver resolver;
+        private ParserSettings defaultSettings;
+        private Map<String, DefaultModuleDescriptor> rawCache = new HashMap<String, DefaultModuleDescriptor>();
+        private Map<String, DefaultModuleDescriptor> augmentedCache = new HashMap<String, DefaultModuleDescriptor>();
+
+        private HashMap<String, String> varReplace = new HashMap<String, String>();
+        private HashMap<String, Pattern> varRegex = new HashMap<String, Pattern>();
+
+        private ModuleDescriptorParser delegate;
+        private String ivyFile = "ivy.xml";
+        private String resolverName;
+        private Pattern keepDependencyPattern;
+        private boolean quiet;
+        private String config;
+
+
+        @Override
+        public boolean accept( Resource res )
+        {
+            boolean accept = ( res instanceof SigilResolver.SigilIvy )
+                || res.getName().endsWith( "/" + SIGIL_BUILDLIST )
+                || ( ( instance.getSigilURI( res ) != null ) && ( ( instance.delegate != null ? instance.delegate
+                    .accept( res ) : false ) || super.accept( res ) ) );
+            if ( accept )
+                Log.verbose( "accepted: " + res );
+            return accept;
+        }
+
+
+        @Override
+        public void toIvyFile( InputStream source, Resource res, File destFile, ModuleDescriptor md )
+            throws ParseException, IOException
+        {
+            Log.verbose( "writing resource: " + res + " toIvyFile: " + destFile );
+            // source allows us to keep layout and comments,
+            // but this doesn't work if source is not ivy.xml.
+            // So just write file directly from descriptor.
+            try
+            {
+                XmlModuleDescriptorWriter.write( md, destFile );
+            }
+            finally
+            {
+                if ( source != null )
+                    source.close();
+            }
+        }
+
+
+        @Override
+        public ModuleDescriptor parseDescriptor( ParserSettings settings, URL xmlURL, boolean validate )
+            throws ParseException, IOException
+        {
+            return parseDescriptor( settings, xmlURL, new URLResource( xmlURL ), validate );
+        }
+
+
+        @Override
+        public ModuleDescriptor parseDescriptor( ParserSettings settings, URL xmlURL, Resource res, boolean validate )
+            throws ParseException, IOException
+        {
+            String cacheKey = xmlURL.toString() + settings.hashCode();
+            DefaultModuleDescriptor dmd = augmentedCache.get( cacheKey );
+
+            if ( dmd == null )
+            {
+                dmd = rawCache.get( cacheKey );
+                if ( dmd == null )
+                {
+                    dmd = delegateParse( settings, xmlURL, res, validate );
+                }
+
+                if ( !quiet )
+                    Log.info( "augmenting module=" + dmd.getModuleRevisionId().getName() + " ant.project.name="
+                        + settings.substitute( "${ant.project.name}" ) );
+
+                addDependenciesToDescriptor( res, dmd );
+                augmentedCache.put( cacheKey, dmd );
+
+                Log.verbose( "augmented dependencies: " + Arrays.asList( dmd.getDependencies() ) );
+            }
+
+            return dmd;
+        }
+
+
+        // used by ProjectRepository
+        ModuleDescriptor parseDescriptor( URL projectURL ) throws ParseException, IOException
+        {
+            if ( defaultSettings == null )
+            {
+                Ivy ivy = IvyContext.getContext().peekIvy();
+                if ( ivy == null )
+                    throw new IllegalStateException( "can't get default settings - no ivy context." );
+                defaultSettings = ivy.getSettings();
+            }
+
+            URL ivyURL = new URL( projectURL, ivyFile );
+            String cacheKey = ivyURL.toString() + defaultSettings.hashCode();
+            DefaultModuleDescriptor dmd = rawCache.get( cacheKey );
+
+            if ( dmd == null )
+            {
+                URLResource res = new URLResource( ivyURL );
+                // Note: this doesn't contain the augmented dependencies, which is OK,
+                // since the ProjectRepository only needs the id and status.
+                dmd = delegateParse( defaultSettings, ivyURL, res, false );
+                rawCache.put( cacheKey, dmd );
+            }
+
+            return dmd;
+        }
+
+
+        private DefaultModuleDescriptor delegateParse( ParserSettings settings, URL xmlURL, Resource res,
+            boolean validate ) throws ParseException, IOException
+        {
+            String resName = res.getName();
+
+            if ( resName.endsWith( "/" + SIGIL_BUILDLIST ) )
+            {
+                String name = resName.substring( 0, resName.length() - SIGIL_BUILDLIST.length() );
+                res = res.clone( name + ivyFile );
+                xmlURL = new URL( xmlURL, ivyFile );
+            }
+
+            if ( settings instanceof IvySettings )
+            {
+                IvySettings ivySettings = ( IvySettings ) settings;
+                String dir = new File( res.getName() ).getParent();
+
+                for ( String name : varRegex.keySet() )
+                {
+                    Pattern regex = varRegex.get( name );
+                    String replace = varReplace.get( name );
+
+                    String value = regex.matcher( dir ).replaceAll( replace );
+
+                    Log.debug( "overriding variable " + name + "=" + value );
+                    ivySettings.setVariable( name, value );
+                }
+            }
+
+            ModuleDescriptor md = null;
+            if ( delegate == null || !delegate.accept( res ) )
+            {
+                md = super.parseDescriptor( settings, xmlURL, res, validate );
+            }
+            else
+            {
+                md = delegate.parseDescriptor( settings, xmlURL, res, validate );
+            }
+
+            return mungeDescriptor( md, res );
+        }
+
+
+        /**
+         * clones descriptor and removes dependencies, as descriptor MUST have
+         * 'this' as the parser given to its constructor.
+         * Only dependency names matched by keepDependencyPattern are kept,
+         * as we're going to add our own dependencies later.
+         */
+        private DefaultModuleDescriptor mungeDescriptor( ModuleDescriptor md, Resource res )
+        {
+
+            if ( ( md instanceof DefaultModuleDescriptor ) && ( md.getParser() == this )
+                && ( KEEP_ALL.equals( keepDependencyPattern ) ) )
+            {
+                return ( DefaultModuleDescriptor ) md;
+            }
+
+            DefaultModuleDescriptor dmd = new DefaultModuleDescriptor( this, res );
+
+            dmd.setModuleRevisionId( md.getModuleRevisionId() );
+            dmd.setPublicationDate( md.getPublicationDate() );
+
+            for ( Configuration c : md.getConfigurations() )
+            {
+                String conf = c.getName();
+
+                dmd.addConfiguration( c );
+
+                for ( Artifact a : md.getArtifacts( conf ) )
+                {
+                    dmd.addArtifact( conf, a );
+                }
+            }
+
+            if ( keepDependencyPattern != null )
+            {
+                for ( DependencyDescriptor dependency : md.getDependencies() )
+                {
+                    String name = dependency.getDependencyId().getName();
+                    if ( keepDependencyPattern.matcher( name ).matches() )
+                    {
+                        dmd.addDependency( dependency );
+                    }
+                }
+            }
+
+            return dmd;
+        }
+
+
+        /*
+         * find URI to Sigil project file. This assumes that it is in the same
+         * directory as the ivy file.
+         */
+        private URI getSigilURI( Resource res )
+        {
+            URI uri = null;
+
+            if ( res instanceof URLResource )
+            {
+                URL url = ( ( URLResource ) res ).getURL();
+                uri = URI.create( url.toString() ).resolve( IBldProject.PROJECT_FILE );
+                try
+                {
+                    InputStream stream = uri.toURL().openStream(); // check file
+                    // exists
+                    stream.close();
+                }
+                catch ( IOException e )
+                {
+                    uri = null;
+                }
+            }
+            else if ( res instanceof FileResource )
+            {
+                uri = ( ( FileResource ) res ).getFile().toURI().resolve( IBldProject.PROJECT_FILE );
+                if ( !( new File( uri ).exists() ) )
+                    uri = null;
+            }
+
+            return uri;
+        }
+
+
+        /*
+         * add sigil dependencies to ModuleDescriptor.
+         */
+        private void addDependenciesToDescriptor( Resource res, DefaultModuleDescriptor md ) throws IOException
+        {
+            // FIXME: transitive should be configurable
+            final boolean transitive = true; // ivy default is true
+            final boolean changing = false;
+            final boolean force = false;
+
+            URI uri = getSigilURI( res );
+            if ( uri == null )
+                return;
+
+            IBldProject project;
+
+            project = BldFactory.getProject( uri );
+
+            IBundleModelElement requirements = project.getDependencies();
+            Log.verbose( "requirements: " + Arrays.asList( requirements.children() ) );
+
+            // preserve version range for Require-Bundle
+            // XXX: synthesise bundle version range corresponding to package version ranges?
+            HashMap<String, VersionRange> versions = new HashMap<String, VersionRange>();
+            for ( IModelElement child : requirements.children() )
+            {
+                if ( child instanceof IRequiredBundle )
+                {
+                    IRequiredBundle bundle = ( IRequiredBundle ) child;
+                    versions.put( bundle.getSymbolicName(), bundle.getVersions() );
+                }
+            }
+
+            IBldResolver resolver = findResolver();
+            if ( resolver == null )
+            {
+                // this can happen in IvyDE, but it retries and is OK next time.
+                Log.warn( "failed to find resolver - IvyContext not yet available." );
+                return;
+            }
+
+            IResolution resolution = resolver.resolve( requirements, false );
+            Log.verbose( "resolution: " + resolution.getBundles() );
+
+            ModuleRevisionId masterMrid = md.getModuleRevisionId();
+            DefaultDependencyDescriptor dd;
+            ModuleRevisionId mrid;
+
+            for ( ISigilBundle bundle : resolution.getBundles() )
+            {
+                IBundleModelElement info = bundle.getBundleInfo();
+                String name = info.getSymbolicName();
+
+                if ( name == null )
+                {
+                    // e.g. SystemProvider with framework=null
+                    continue;
+                }
+
+                if ( bundle instanceof ProjectRepository.ProjectBundle )
+                {
+                    ProjectRepository.ProjectBundle pb = ( ProjectRepository.ProjectBundle ) bundle;
+                    String org = pb.getOrg();
+                    if ( org == null )
+                        org = masterMrid.getOrganisation();
+                    String id = pb.getId();
+                    String module = pb.getModule();
+                    String rev = pb.getRevision();
+
+                    mrid = ModuleRevisionId.newInstance( org, module, rev );
+                    dd = new SigilDependencyDescriptor( md, mrid, force, changing, transitive );
+
+                    if ( !id.equals( module ) )
+                    { // non-default artifact
+                        dd.addDependencyArtifact( module, new DefaultDependencyArtifactDescriptor( dd, id, "jar",
+                            "jar", null, null ) );
+                    }
+                }
+                else
+                {
+                    VersionRange version = versions.get( name );
+                    String rev = version != null ? version.toString() : info.getVersion().toString();
+                    mrid = ModuleRevisionId.newInstance( SigilResolver.ORG_SIGIL, name, rev );
+                    dd = new SigilDependencyDescriptor( md, mrid, force, changing, transitive );
+                }
+
+                int nDeps = 0;
+                boolean foundDefault = false;
+
+                // TODO: make dependency configurations configurable SIGIL-176
+                for ( String conf : md.getConfigurationsNames() )
+                {
+                    if ( conf.equals( "default" ) )
+                    {
+                        foundDefault = true;
+                    }
+                    else if ( md.getArtifacts( conf ).length == 0 )
+                    {
+                        dd.addDependencyConfiguration( conf, conf + "(default)" );
+                        nDeps++;
+                    }
+                }
+
+                if ( nDeps > 0 )
+                {
+                    if ( foundDefault )
+                        dd.addDependencyConfiguration( "default", "default" );
+                }
+                else
+                {
+                    dd.addDependencyConfiguration( "*", "*" ); // all configurations
+                }
+
+                md.addDependency( dd );
+            }
+
+            boolean resolved = true;
+            for ( IModelElement child : requirements.children() )
+            {
+                ISigilBundle provider = resolution.getProvider( child );
+                if ( provider == null )
+                {
+                    resolved = false;
+                    // this is parse phase, so only log verbose message.
+                    // error is produced during resolution phase.
+                    Log.verbose( "WARN: can't resolve: " + child );
+
+                    String name;
+                    String version;
+                    if ( child instanceof IRequiredBundle )
+                    {
+                        IRequiredBundle rb = ( IRequiredBundle ) child;
+                        name = rb.getSymbolicName();
+                        version = rb.getVersions().toString();
+                    }
+                    else
+                    {
+                        IPackageImport pi = ( IPackageImport ) child;
+                        name = "import!" + pi.getPackageName();
+                        version = pi.getVersions().toString();
+                    }
+
+                    mrid = ModuleRevisionId.newInstance( "!" + SigilResolver.ORG_SIGIL, name, version );
+                    dd = new SigilDependencyDescriptor( md, mrid, force, changing, transitive );
+                    dd.addDependencyConfiguration( "*", "*" ); // all
+                    // configurations
+                    md.addDependency( dd );
+                }
+            }
+
+            if ( !resolved )
+            {
+                // Ivy does not show warnings or errors logged from parse phase, until after resolution.
+                // but <buildlist> ant task doesn't do resolution, so errors would be silently ignored.
+                // Hence, we must use Message.info to ensure this failure is seen.
+                if ( !quiet )
+                    Log.info( "WARN: resolution failed in: " + masterMrid.getName() + ". Use -v for details." );
+            }
+        }
+
+
+        /*
+         * find named resolver, or first resolver that implements IBldResolver.
+         */
+        private IBldResolver findResolver()
+        {
+            if ( resolver == null )
+            {
+                Ivy ivy = IvyContext.getContext().peekIvy();
+                if ( ivy != null )
+                {
+                    if ( resolverName != null )
+                    {
+                        DependencyResolver r = ivy.getSettings().getResolver( resolverName );
+                        if ( r == null )
+                        {
+                            throw new Error( "SigilParser: resolver \"" + resolverName + "\" not defined." );
+                        }
+                        else if ( r instanceof IBldResolver )
+                        {
+                            resolver = ( IBldResolver ) r;
+                        }
+                        else
+                        {
+                            throw new Error( "SigilParser: resolver \"" + resolverName + "\" is not a SigilResolver." );
+                        }
+                    }
+                    else
+                    {
+                        for ( Object r : ivy.getSettings().getResolvers() )
+                        {
+                            if ( r instanceof IBldResolver )
+                            {
+                                resolver = ( IBldResolver ) r;
+                                break;
+                            }
+                        }
+                    }
+
+                    if ( resolver == null )
+                    {
+                        throw new Error( "SigilParser: can't find SigilResolver. Check ivysettings.xml." );
+                    }
+                }
+                else if ( config != null )
+                {
+                    Log.warn( "creating duplicate resolver to support IvyDE." );
+                    resolver = new SigilResolver();
+                    ( ( SigilResolver ) resolver ).setConfig( config );
+                }
+            }
+            return resolver;
+        }
+    }
 
 }
 
@@ -579,9 +698,11 @@
  * this is only needed so that we can distinguish sigil-added dependencies from
  * existing ones.
  */
-class SigilDependencyDescriptor extends DefaultDependencyDescriptor {
-	public SigilDependencyDescriptor(DefaultModuleDescriptor md, ModuleRevisionId mrid, boolean force,
-			boolean changing, boolean transitive) {
-		super(md, mrid, force, changing, transitive);
-	}
+class SigilDependencyDescriptor extends DefaultDependencyDescriptor
+{
+    public SigilDependencyDescriptor( DefaultModuleDescriptor md, ModuleRevisionId mrid, boolean force,
+        boolean changing, boolean transitive )
+    {
+        super( md, mrid, force, changing, transitive );
+    }
 }

Modified: felix/trunk/sigil/ivy/resolver/src/org/apache/felix/sigil/ivy/SigilResolver.java
URL: http://svn.apache.org/viewvc/felix/trunk/sigil/ivy/resolver/src/org/apache/felix/sigil/ivy/SigilResolver.java?rev=796467&r1=796466&r2=796467&view=diff
==============================================================================
--- felix/trunk/sigil/ivy/resolver/src/org/apache/felix/sigil/ivy/SigilResolver.java (original)
+++ felix/trunk/sigil/ivy/resolver/src/org/apache/felix/sigil/ivy/SigilResolver.java Tue Jul 21 18:51:33 2009
@@ -19,6 +19,7 @@
 
 package org.apache.felix.sigil.ivy;
 
+
 import static java.lang.String.format;
 
 import java.io.File;
@@ -54,359 +55,464 @@
 import org.apache.ivy.plugins.resolver.util.ResolvedResource;
 import org.apache.ivy.util.FileUtil;
 
+
 /**
  * This resolver is able to work with Sigil repositories.
  * It does not allow publishing.
  */
-public class SigilResolver extends BasicResolver implements IBldResolver {
-	/** the sigil-injected dependency organisation name */
-	static final String ORG_SIGIL = "sigil";
-	
-	private IBldResolver resolver;
-	private String config;
-	private boolean extractBCP = false;
-	private Map<String, Resource> resourcesCache = new HashMap<String, Resource>();
-	
-	/**
-	 * no-args constructor required by Ivy.
-	 * 
-	 * XXX: It doesn't currently seem to matter that Ivy instantiates this many times,
-	 * since SigilParser caches it, and the ProjectRepositoryProvider static cache
-	 * prevents it being re-loaded unnecessarily.
-	 * 
-	 * If this should become a problem, then we will need to delegate to a singleton instance,
-	 * as we have done in SigilParser.
-	 */
-	public SigilResolver() {
-	}
-	
-	public void setConfig(String config) {
-		this.config = config;
-	}
-	
-	/**
-	 * if true, resolver extracts any jars embedded in Bundle-ClassPath.
-	 */
-	public void setExtractBCP(String extract) {
-		this.extractBCP = Boolean.parseBoolean(extract);
-	}
-
-	private IBldResolver getResolver() {
-		if (resolver == null) {
-			if (config == null) {
-				throw new Error("SigilResolver: not configured. Specify config=\"PATH\" in ivysettings.xml.");
-			}
-			try {
-				URI uri;
-				File file = new File(config);
-				
-				if (file.isAbsolute()) {
-					uri = file.toURI();
-				} else {
-    				URI cwd = new File(".").toURI();
-    				uri = cwd.resolve(config);
-				}
-				
-				Map<String, Properties> repositories = BldFactory.getConfig(uri).getRepositoryConfig();
-				resolver = new BldResolver(repositories);
-			} catch (IOException e) {
-				throw new Error("SigilResolver: failed to configure: " + e);
-			}
-		}
-		return resolver;
-	}
-
-	public IResolution resolveOrFail(IModelElement element, boolean transitive) throws ResolutionException {
-		return getResolver().resolveOrFail(element, transitive);
-	}
-
-	public IResolution resolve(IModelElement element, boolean transitive) {
-		return getResolver().resolve(element, transitive);
-	}
-
-	public String getTypeName() {
-		return "sigil";
-	}
-
-	/*
-	 * synthesize an ivy descriptor for a Sigil dependency. called after Sigil
-	 * resolution, so descriptor already contains resolved version.
-	 */
-	public ResolvedResource findIvyFileRef(DependencyDescriptor dd, ResolveData data) {
-		ResolvedResource ref = null;
-
-		ModuleRevisionId id = dd.getDependencyRevisionId();
-
-		if (!id.getOrganisation().equals(ORG_SIGIL)) {
-			return null;
-		}
-
-		ISigilBundle bundle = resolve(id);
-		if (bundle == null) {
-			Log.error( "Failed to find bundle for module " + id );
-			return null;
-		}
-
-		String symbolicName = id.getName();
-		String version = bundle.getVersion().toString();
-
-		Resource res = new SigilIvy(extractBCP ? bundle : null, symbolicName, version);
-		ref = new ResolvedResource(res, version);
-
-		Log.debug(format("findIvyFileRef: dd=%s => ref=%s", dd, ref));
-		return ref;
-	}
-
-	@Override
-	protected ResolvedResource findArtifactRef(Artifact artifact, Date date) {
-		String name = artifact.getName();
-		ModuleRevisionId id = artifact.getModuleRevisionId();
-
-		if (!id.getOrganisation().equals(ORG_SIGIL)) {
-			return null;
-		}
-		
-		ISigilBundle bundle = resolve(id);
-		if (bundle == null) {
-			return null;
-		}
-
-		IBundleModelElement info = bundle.getBundleInfo();
-		URI uri = info.getUpdateLocation();
-		Resource res = null;
-
-		try {
-			URL url =  (uri != null) ? uri.toURL() : bundle.getLocation().toFile().toURL();
-    		if (name.contains("!")) {
-    			String[] split = name.split("!");
-    			url = new URL("jar:" + url + "!/" + split[1] + "." + artifact.getExt());
-    		}
-			res = new URLResource(url);
-		} catch (MalformedURLException e) {
-			System.out.println("Oops! " + e);
-		}
-
-		String version = bundle.getVersion().toString();
-		ResolvedResource ref = new ResolvedResource(res, version);
-
-		Log.debug(format("findArtifactRef: artifact=%s, date=%s => ref=%s", artifact, date, ref));
-		return ref;
-	}
-
-	private ISigilBundle resolve(ModuleRevisionId id) {
-		String revision = id.getRevision();
-		String range = revision;
-
-		if (revision.indexOf(',') < 0) {
-			// SigilParser has already resolved the revision from the import
-			// version range.
-			// We now need to locate the same bundle to get its download URL.
-			// We must use an OSGi version range to specify the exact version,
-			// otherwise it will resolve to "specified version or above".
-			range = "[" + revision + "," + revision + "]";
-		}
-
-		RequiredBundle bundle = new RequiredBundle();
-		bundle.setSymbolicName(id.getName());
-		bundle.setVersions(VersionRange.parseVersionRange(range));
-
-		Log.verbose("searching for " + bundle);
-		
-		try {
-			IResolution resolution = resolveOrFail(bundle, false);
-			ISigilBundle[] bundles = resolution.getBundles().toArray(new ISigilBundle[0]);
-			if (bundles.length == 1) {
-				return bundles[0];
-			}
-		} catch (ResolutionException e) {
-			Log.warn( e.getMessage() );
-			return null;
-		}
-		return null;
-	}
-
-	/*
-	 * Implement BasicResolver abstract methods
-	 */
-
-	@Override
-	protected long get(Resource res, File dest) throws IOException {
-		FileUtil.copy(res.openStream(), dest, null);
-		long len = res.getContentLength();
-		Log.debug(format("get(%s, %s) = %d", res, dest, len));
-		return len;
-	}
-
-
-	@Override
-	public Resource getResource(String source) throws IOException {
-		source = encode(source);
-		Resource res = resourcesCache.get(source);
-		if (res == null) {
-			res = new URLResource(new URL(source));
-			resourcesCache.put(source, res);
-		}
-		Log.debug(format("SIGIL: getResource(%s) = %d", source, res));
-		return res;
-	}
-
-	private static String encode(String source) {
-		return source.trim().replaceAll(" ", "%20");
-	}
-
-	@SuppressWarnings("unchecked")
-	@Override
-	protected Collection findNames(Map tokenValues, String token) {
-		throw new Error("SigilResolver: findNames not supported.");
-	}
-
-	public void publish(Artifact artifact, File src, boolean overwrite) throws IOException {
-		throw new Error("SigilResolver: publish not supported.");
-	}
-
-	public void beginPublishTransaction(ModuleRevisionId module, boolean overwrite) throws IOException {
-		// stop publish attempts being silently ignored.
-		throw new Error("SigilResolver: publish not supported.");
-	}
-
-	/*
-	 * Synthesize a virtual ivy file for a Sigil dependency.
-	 */
-	static class SigilIvy implements Resource {
-		private StringBuilder content = new StringBuilder();
-		private String name;
-		private boolean exists = true;
-
-		private SigilIvy() {
-		}
-
-		public SigilIvy(ISigilBundle bundle, String module, String rev) {
-			this.name = "sigil!" + module + "#" + rev;
-
-			String org = ORG_SIGIL;
-			// FIXME: make status and pub configurable
-			String status = "release";
-			String pub = "20080912162859";
-
-			content.append("<ivy-module version=\"1.0\">\n");
-
-			content.append(format(
-					"<info organisation=\"%s\" module=\"%s\" revision=\"%s\" status=\"%s\" publication=\"%s\"/>\n",
-					org, module, rev, status, pub));
-			
-			String bcp = readBundleClassPath(bundle);
-			if (bcp != null) {
-    			content.append("<publications>\n");
-    			for (String j : bcp.split(",\\s*")) {
-    				if (j.equals(".")) {
-            			content.append("<artifact/>\n");
-        			} else if (j.endsWith(".jar") && bundleContains(bundle, j)) {
-        				j = j.substring(0, j.length() - 4);
-            			content.append(format("<artifact name=\"%s!%s\"/>\n", module, j));
-        			}
-    			}
-    			content.append("</publications>\n");
-			}
-
-			// TODO: add dependencies?
-			// <dependencies>
-			// <dependency org="org.apache" name="log4j" rev="1.2.12"
-			// revConstraint="[1.2,1.3)"/>
-			// </dependencies>
-
-			content.append("</ivy-module>\n");
-		}
-		
-        private boolean bundleContains(ISigilBundle bundle, String j) {
-        	URI uri = bundle.getBundleInfo().getUpdateLocation();
+public class SigilResolver extends BasicResolver implements IBldResolver
+{
+    /** the sigil-injected dependency organisation name */
+    static final String ORG_SIGIL = "sigil";
+
+    private IBldResolver resolver;
+    private String config;
+    private boolean extractBCP = false;
+    private Map<String, Resource> resourcesCache = new HashMap<String, Resource>();
+
+
+    /**
+     * no-args constructor required by Ivy.
+     * 
+     * XXX: It doesn't currently seem to matter that Ivy instantiates this many times,
+     * since SigilParser caches it, and the ProjectRepositoryProvider static cache
+     * prevents it being re-loaded unnecessarily.
+     * 
+     * If this should become a problem, then we will need to delegate to a singleton instance,
+     * as we have done in SigilParser.
+     */
+    public SigilResolver()
+    {
+    }
+
+
+    public void setConfig( String config )
+    {
+        this.config = config;
+    }
+
+
+    /**
+     * if true, resolver extracts any jars embedded in Bundle-ClassPath.
+     */
+    public void setExtractBCP( String extract )
+    {
+        this.extractBCP = Boolean.parseBoolean( extract );
+    }
+
+
+    private IBldResolver getResolver()
+    {
+        if ( resolver == null )
+        {
+            if ( config == null )
+            {
+                throw new Error( "SigilResolver: not configured. Specify config=\"PATH\" in ivysettings.xml." );
+            }
+            try
+            {
+                URI uri;
+                File file = new File( config );
+
+                if ( file.isAbsolute() )
+                {
+                    uri = file.toURI();
+                }
+                else
+                {
+                    URI cwd = new File( "." ).toURI();
+                    uri = cwd.resolve( config );
+                }
+
+                Map<String, Properties> repositories = BldFactory.getConfig( uri ).getRepositoryConfig();
+                resolver = new BldResolver( repositories );
+            }
+            catch ( IOException e )
+            {
+                throw new Error( "SigilResolver: failed to configure: " + e );
+            }
+        }
+        return resolver;
+    }
+
+
+    public IResolution resolveOrFail( IModelElement element, boolean transitive ) throws ResolutionException
+    {
+        return getResolver().resolveOrFail( element, transitive );
+    }
+
+
+    public IResolution resolve( IModelElement element, boolean transitive )
+    {
+        return getResolver().resolve( element, transitive );
+    }
+
+
+    public String getTypeName()
+    {
+        return "sigil";
+    }
+
+
+    /*
+     * synthesize an ivy descriptor for a Sigil dependency. called after Sigil
+     * resolution, so descriptor already contains resolved version.
+     */
+    public ResolvedResource findIvyFileRef( DependencyDescriptor dd, ResolveData data )
+    {
+        ResolvedResource ref = null;
+
+        ModuleRevisionId id = dd.getDependencyRevisionId();
+
+        if ( !id.getOrganisation().equals( ORG_SIGIL ) )
+        {
+            return null;
+        }
+
+        ISigilBundle bundle = resolve( id );
+        if ( bundle == null )
+        {
+            Log.error( "Failed to find bundle for module " + id );
+            return null;
+        }
+
+        String symbolicName = id.getName();
+        String version = bundle.getVersion().toString();
+
+        Resource res = new SigilIvy( extractBCP ? bundle : null, symbolicName, version );
+        ref = new ResolvedResource( res, version );
+
+        Log.debug( format( "findIvyFileRef: dd=%s => ref=%s", dd, ref ) );
+        return ref;
+    }
+
+
+    @Override
+    protected ResolvedResource findArtifactRef( Artifact artifact, Date date )
+    {
+        String name = artifact.getName();
+        ModuleRevisionId id = artifact.getModuleRevisionId();
+
+        if ( !id.getOrganisation().equals( ORG_SIGIL ) )
+        {
+            return null;
+        }
+
+        ISigilBundle bundle = resolve( id );
+        if ( bundle == null )
+        {
+            return null;
+        }
+
+        IBundleModelElement info = bundle.getBundleInfo();
+        URI uri = info.getUpdateLocation();
+        Resource res = null;
+
+        try
+        {
+            URL url = ( uri != null ) ? uri.toURL() : bundle.getLocation().toFile().toURL();
+            if ( name.contains( "!" ) )
+            {
+                String[] split = name.split( "!" );
+                url = new URL( "jar:" + url + "!/" + split[1] + "." + artifact.getExt() );
+            }
+            res = new URLResource( url );
+        }
+        catch ( MalformedURLException e )
+        {
+            System.out.println( "Oops! " + e );
+        }
+
+        String version = bundle.getVersion().toString();
+        ResolvedResource ref = new ResolvedResource( res, version );
+
+        Log.debug( format( "findArtifactRef: artifact=%s, date=%s => ref=%s", artifact, date, ref ) );
+        return ref;
+    }
+
+
+    private ISigilBundle resolve( ModuleRevisionId id )
+    {
+        String revision = id.getRevision();
+        String range = revision;
+
+        if ( revision.indexOf( ',' ) < 0 )
+        {
+            // SigilParser has already resolved the revision from the import
+            // version range.
+            // We now need to locate the same bundle to get its download URL.
+            // We must use an OSGi version range to specify the exact version,
+            // otherwise it will resolve to "specified version or above".
+            range = "[" + revision + "," + revision + "]";
+        }
+
+        RequiredBundle bundle = new RequiredBundle();
+        bundle.setSymbolicName( id.getName() );
+        bundle.setVersions( VersionRange.parseVersionRange( range ) );
+
+        Log.verbose( "searching for " + bundle );
+
+        try
+        {
+            IResolution resolution = resolveOrFail( bundle, false );
+            ISigilBundle[] bundles = resolution.getBundles().toArray( new ISigilBundle[0] );
+            if ( bundles.length == 1 )
+            {
+                return bundles[0];
+            }
+        }
+        catch ( ResolutionException e )
+        {
+            Log.warn( e.getMessage() );
+            return null;
+        }
+        return null;
+    }
+
+
+    /*
+     * Implement BasicResolver abstract methods
+     */
+
+    @Override
+    protected long get( Resource res, File dest ) throws IOException
+    {
+        FileUtil.copy( res.openStream(), dest, null );
+        long len = res.getContentLength();
+        Log.debug( format( "get(%s, %s) = %d", res, dest, len ) );
+        return len;
+    }
+
+
+    @Override
+    public Resource getResource( String source ) throws IOException
+    {
+        source = encode( source );
+        Resource res = resourcesCache.get( source );
+        if ( res == null )
+        {
+            res = new URLResource( new URL( source ) );
+            resourcesCache.put( source, res );
+        }
+        Log.debug( format( "SIGIL: getResource(%s) = %d", source, res ) );
+        return res;
+    }
+
+
+    private static String encode( String source )
+    {
+        return source.trim().replaceAll( " ", "%20" );
+    }
+
+
+    @SuppressWarnings("unchecked")
+    @Override
+    protected Collection findNames( Map tokenValues, String token )
+    {
+        throw new Error( "SigilResolver: findNames not supported." );
+    }
+
+
+    public void publish( Artifact artifact, File src, boolean overwrite ) throws IOException
+    {
+        throw new Error( "SigilResolver: publish not supported." );
+    }
+
+
+    public void beginPublishTransaction( ModuleRevisionId module, boolean overwrite ) throws IOException
+    {
+        // stop publish attempts being silently ignored.
+        throw new Error( "SigilResolver: publish not supported." );
+    }
+
+    /*
+     * Synthesize a virtual ivy file for a Sigil dependency.
+     */
+    static class SigilIvy implements Resource
+    {
+        private StringBuilder content = new StringBuilder();
+        private String name;
+        private boolean exists = true;
+
+
+        private SigilIvy()
+        {
+        }
+
+
+        public SigilIvy( ISigilBundle bundle, String module, String rev )
+        {
+            this.name = "sigil!" + module + "#" + rev;
+
+            String org = ORG_SIGIL;
+            // FIXME: make status and pub configurable
+            String status = "release";
+            String pub = "20080912162859";
+
+            content.append( "<ivy-module version=\"1.0\">\n" );
+
+            content.append( format(
+                "<info organisation=\"%s\" module=\"%s\" revision=\"%s\" status=\"%s\" publication=\"%s\"/>\n", org,
+                module, rev, status, pub ) );
+
+            String bcp = readBundleClassPath( bundle );
+            if ( bcp != null )
+            {
+                content.append( "<publications>\n" );
+                for ( String j : bcp.split( ",\\s*" ) )
+                {
+                    if ( j.equals( "." ) )
+                    {
+                        content.append( "<artifact/>\n" );
+                    }
+                    else if ( j.endsWith( ".jar" ) && bundleContains( bundle, j ) )
+                    {
+                        j = j.substring( 0, j.length() - 4 );
+                        content.append( format( "<artifact name=\"%s!%s\"/>\n", module, j ) );
+                    }
+                }
+                content.append( "</publications>\n" );
+            }
+
+            // TODO: add dependencies?
+            // <dependencies>
+            // <dependency org="org.apache" name="log4j" rev="1.2.12"
+            // revConstraint="[1.2,1.3)"/>
+            // </dependencies>
+
+            content.append( "</ivy-module>\n" );
+        }
+
+
+        private boolean bundleContains( ISigilBundle bundle, String j )
+        {
+            URI uri = bundle.getBundleInfo().getUpdateLocation();
             InputStream is = null;
-			try {
-    			URL url =  (uri != null) ? uri.toURL() : bundle.getLocation().toFile().toURL();
-				is = url.openStream();
-            	JarInputStream js = new JarInputStream(is, false);
-            	JarEntry entry;
-            	while ( (entry = js.getNextJarEntry()) != null ) {
-            		if ( j.equals( entry.getName() ) ) {
-            			return true;
-            		}
-            	}
-			} catch (IOException e) {
-			} finally {
-				if (is != null) {
-					try {
-						is.close();
-					} catch (IOException e2) {
-					}
-				}
-			}
-        	return false;
-		}
-
-		private String readBundleClassPath(ISigilBundle bundle) {
-        	if (bundle == null)
-        		return null;
-        	
-        	URI uri = bundle.getBundleInfo().getUpdateLocation();
+            try
+            {
+                URL url = ( uri != null ) ? uri.toURL() : bundle.getLocation().toFile().toURL();
+                is = url.openStream();
+                JarInputStream js = new JarInputStream( is, false );
+                JarEntry entry;
+                while ( ( entry = js.getNextJarEntry() ) != null )
+                {
+                    if ( j.equals( entry.getName() ) )
+                    {
+                        return true;
+                    }
+                }
+            }
+            catch ( IOException e )
+            {
+            }
+            finally
+            {
+                if ( is != null )
+                {
+                    try
+                    {
+                        is.close();
+                    }
+                    catch ( IOException e2 )
+                    {
+                    }
+                }
+            }
+            return false;
+        }
+
+
+        private String readBundleClassPath( ISigilBundle bundle )
+        {
+            if ( bundle == null )
+                return null;
+
+            URI uri = bundle.getBundleInfo().getUpdateLocation();
             InputStream is = null;
-			try {
-    			URL url =  (uri != null) ? uri.toURL() : bundle.getLocation().toFile().toURL();
-				is = url.openStream();
-            	JarInputStream js = new JarInputStream(is, false);
+            try
+            {
+                URL url = ( uri != null ) ? uri.toURL() : bundle.getLocation().toFile().toURL();
+                is = url.openStream();
+                JarInputStream js = new JarInputStream( is, false );
                 Manifest m = js.getManifest();
-                if (m != null)
-                    return (String) m.getMainAttributes().getValue("Bundle-ClassPath");
-			} catch (IOException e) {
-			} finally {
-				if (is != null) {
-					try {
-						is.close();
-					} catch (IOException e2) {
-					}
-				}
-			}
-			
+                if ( m != null )
+                    return ( String ) m.getMainAttributes().getValue( "Bundle-ClassPath" );
+            }
+            catch ( IOException e )
+            {
+            }
+            finally
+            {
+                if ( is != null )
+                {
+                    try
+                    {
+                        is.close();
+                    }
+                    catch ( IOException e2 )
+                    {
+                    }
+                }
+            }
+
             return null;
         }
 
-		public String toString() {
-			return "SigilIvy[" + name + "]";
-		}
-
-		// clone is used to read checksum files
-		// so clone(getName() + ".sha1").exists() should be false
-		public Resource clone(String cloneName) {
-			Log.debug("SigilIvy: clone: " + cloneName);
-			SigilIvy clone = new SigilIvy();
-			clone.name = cloneName;
-			clone.exists = false;
-			return clone;
-		}
-
-		public boolean exists() {
-			return exists;
-		}
-
-		public long getContentLength() {
-			return content.length();
-		}
-
-		public long getLastModified() {
-			// TODO Auto-generated method stub
-			Log.debug("NOT IMPLEMENTED: getLastModified");
-			return 0;
-		}
-
-		public String getName() {
-			return name;
-		}
-
-		public boolean isLocal() {
-			return false;
-		}
-
-		@SuppressWarnings("deprecation")
-		public InputStream openStream() throws IOException {
-			return new java.io.StringBufferInputStream(content.toString());
-		}
-	}
+
+        public String toString()
+        {
+            return "SigilIvy[" + name + "]";
+        }
+
+
+        // clone is used to read checksum files
+        // so clone(getName() + ".sha1").exists() should be false
+        public Resource clone( String cloneName )
+        {
+            Log.debug( "SigilIvy: clone: " + cloneName );
+            SigilIvy clone = new SigilIvy();
+            clone.name = cloneName;
+            clone.exists = false;
+            return clone;
+        }
+
+
+        public boolean exists()
+        {
+            return exists;
+        }
+
+
+        public long getContentLength()
+        {
+            return content.length();
+        }
+
+
+        public long getLastModified()
+        {
+            // TODO Auto-generated method stub
+            Log.debug( "NOT IMPLEMENTED: getLastModified" );
+            return 0;
+        }
+
+
+        public String getName()
+        {
+            return name;
+        }
+
+
+        public boolean isLocal()
+        {
+            return false;
+        }
+
+
+        @SuppressWarnings("deprecation")
+        public InputStream openStream() throws IOException
+        {
+            return new java.io.StringBufferInputStream( content.toString() );
+        }
+    }
 }

Modified: felix/trunk/sigil/sigil-builder/src/org/apache/felix/sigil/build/Feature.java
URL: http://svn.apache.org/viewvc/felix/trunk/sigil/sigil-builder/src/org/apache/felix/sigil/build/Feature.java?rev=796467&r1=796466&r2=796467&view=diff
==============================================================================
--- felix/trunk/sigil/sigil-builder/src/org/apache/felix/sigil/build/Feature.java (original)
+++ felix/trunk/sigil/sigil-builder/src/org/apache/felix/sigil/build/Feature.java Tue Jul 21 18:51:33 2009
@@ -21,7 +21,9 @@
  */
 package org.apache.felix.sigil.build;
 
-class Feature {
-	String id, version, url;
-	String[] categories;
+
+class Feature
+{
+    String id, version, url;
+    String[] categories;
 }

Modified: felix/trunk/sigil/sigil-builder/src/org/apache/felix/sigil/build/FindBundlesTask.java
URL: http://svn.apache.org/viewvc/felix/trunk/sigil/sigil-builder/src/org/apache/felix/sigil/build/FindBundlesTask.java?rev=796467&r1=796466&r2=796467&view=diff
==============================================================================
--- felix/trunk/sigil/sigil-builder/src/org/apache/felix/sigil/build/FindBundlesTask.java (original)
+++ felix/trunk/sigil/sigil-builder/src/org/apache/felix/sigil/build/FindBundlesTask.java Tue Jul 21 18:51:33 2009
@@ -18,6 +18,7 @@
  */
 package org.apache.felix.sigil.build;
 
+
 import java.io.File;
 import java.io.FilenameFilter;
 
@@ -25,77 +26,98 @@
 import org.apache.tools.ant.Task;
 import org.osgi.framework.Version;
 
-public class FindBundlesTask extends Task {
 
-	private File dir;
-	private String symbolicName;
-	private String property;
-
-	public File getDir() {
-		return dir;
-	}
-
-	public void setDir(File dir) {
-		this.dir = dir;
-	}
-
-	public String getSymbolicName() {
-		return symbolicName;
-	}
-
-	public void setSymbolicName(String symbolicName) {
-		this.symbolicName = symbolicName;
-	}
-
-	public String getProperty() {
-		return property;
-	}
-
-	public void setProperty(String property) {
-		this.property = property;
-	}
-
-	@Override
-	public void execute() throws BuildException {
-		System.out.println("Searching " + dir + " for bundle '" + symbolicName + "'");
-		final String prefix = symbolicName + "_";
-		String[] files = dir.list(new FilenameFilter() {
-			public boolean accept(File dir, String name) {
-				return name.startsWith(prefix);
-			}
-		});
-		if (files == null)
-		    files = new String[0];
-
-		System.out.println("Found " + files.length + " file(s) starting with " + symbolicName);
-
-		Version highest = null;
-		for (String filename : files) {
-			System.out.println("Testing " + filename);
-			// Drop the prefix
-			int startIndex = prefix.length();
-
-			// Drop the ".jar" suffix if present
-			int endIndex = filename.length();
-			if (filename.toLowerCase().endsWith(".jar")) {
-				endIndex -= 4;
-			}
-
-			String versionString = filename.substring(startIndex, endIndex);
-			System.out.println("Version string is '" + versionString + "'");
-
-			Version version = new Version(versionString);
-			if (highest == null || version.compareTo(highest) > 0) {
-				highest = version;
-			}
-		}
-
-		if (highest == null) {
-			throw new BuildException("No matches for symbolic name '"
-					+ symbolicName + "'");
-		}
+public class FindBundlesTask extends Task
+{
+
+    private File dir;
+    private String symbolicName;
+    private String property;
+
+
+    public File getDir()
+    {
+        return dir;
+    }
+
+
+    public void setDir( File dir )
+    {
+        this.dir = dir;
+    }
+
+
+    public String getSymbolicName()
+    {
+        return symbolicName;
+    }
+
+
+    public void setSymbolicName( String symbolicName )
+    {
+        this.symbolicName = symbolicName;
+    }
+
+
+    public String getProperty()
+    {
+        return property;
+    }
+
+
+    public void setProperty( String property )
+    {
+        this.property = property;
+    }
+
+
+    @Override
+    public void execute() throws BuildException
+    {
+        System.out.println( "Searching " + dir + " for bundle '" + symbolicName + "'" );
+        final String prefix = symbolicName + "_";
+        String[] files = dir.list( new FilenameFilter()
+        {
+            public boolean accept( File dir, String name )
+            {
+                return name.startsWith( prefix );
+            }
+        } );
+        if ( files == null )
+            files = new String[0];
+
+        System.out.println( "Found " + files.length + " file(s) starting with " + symbolicName );
+
+        Version highest = null;
+        for ( String filename : files )
+        {
+            System.out.println( "Testing " + filename );
+            // Drop the prefix
+            int startIndex = prefix.length();
+
+            // Drop the ".jar" suffix if present
+            int endIndex = filename.length();
+            if ( filename.toLowerCase().endsWith( ".jar" ) )
+            {
+                endIndex -= 4;
+            }
+
+            String versionString = filename.substring( startIndex, endIndex );
+            System.out.println( "Version string is '" + versionString + "'" );
+
+            Version version = new Version( versionString );
+            if ( highest == null || version.compareTo( highest ) > 0 )
+            {
+                highest = version;
+            }
+        }
+
+        if ( highest == null )
+        {
+            throw new BuildException( "No matches for symbolic name '" + symbolicName + "'" );
+        }
 
-		getProject().setNewProperty(property, highest.toString());
-	}
+        getProject().setNewProperty( property, highest.toString() );
+    }
 
 }

Modified: felix/trunk/sigil/sigil-builder/src/org/apache/felix/sigil/build/SiteInsertFeatureContentHandler.java
URL: http://svn.apache.org/viewvc/felix/trunk/sigil/sigil-builder/src/org/apache/felix/sigil/build/SiteInsertFeatureContentHandler.java?rev=796467&r1=796466&r2=796467&view=diff
==============================================================================
--- felix/trunk/sigil/sigil-builder/src/org/apache/felix/sigil/build/SiteInsertFeatureContentHandler.java (original)
+++ felix/trunk/sigil/sigil-builder/src/org/apache/felix/sigil/build/SiteInsertFeatureContentHandler.java Tue Jul 21 18:51:33 2009
@@ -18,6 +18,7 @@
  */
 package org.apache.felix.sigil.build;
 
+
 import java.util.List;
 
 import org.xml.sax.Attributes;
@@ -26,79 +27,106 @@
 import org.xml.sax.SAXException;
 import org.xml.sax.helpers.AttributesImpl;
 
-class SiteInsertFeatureContentHandler implements ContentHandler {
-	
-	private final ContentHandler output;
-	private final List<org.apache.felix.sigil.build.Feature> featureList;
-
-	public SiteInsertFeatureContentHandler(ContentHandler output,
-			List<Feature> featureList) {
-		this.output = output;
-		this.featureList = featureList;
-	}
-
-	public void characters(char[] ch, int start, int length) throws SAXException {
-		output.characters(ch, start, length);
-	}
-
-	public void endDocument() throws SAXException {
-		output.endDocument();
-	}
-
-	public void endElement(String uri, String localName, String name) throws SAXException {
-		output.endElement(uri, localName, name);
-	}
-
-	public void endPrefixMapping(String prefix) throws SAXException {
-		output.endPrefixMapping(prefix);
-	}
-
-	public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
-		//output.ignorableWhitespace(ch, start, length);
-	}
-
-	public void processingInstruction(String target, String data) throws SAXException {
-		output.processingInstruction(target, data);
-	}
-
-	public void setDocumentLocator(Locator locator) {
-		output.setDocumentLocator(locator);
-	}
-
-	public void skippedEntity(String name) throws SAXException {
-		output.skippedEntity(name);
-	}
-
-	public void startDocument() throws SAXException {
-		output.startDocument();
-	}
-
-	public void startElement(String uri, String localName, String name, Attributes atts)
-			throws SAXException {
-		output.startElement(uri, localName, name, atts);
-		
-		if("site".equals(name)) {
-			for (Feature feature : featureList) {
-				AttributesImpl featureAtts = new AttributesImpl();
-				featureAtts.addAttribute("", "", "url", "CDATA", feature.url);
-				featureAtts.addAttribute("", "", "id", "CDATA", feature.id);
-				featureAtts.addAttribute("", "", "version", "CDATA", feature.version);
-				output.startElement("", "", "feature", featureAtts);
-				
-				for (int i = 0; i < feature.categories.length; i++) {
-					AttributesImpl categoryAtts = new AttributesImpl();
-					categoryAtts.addAttribute("", "", "name", "CDATA", feature.categories[i]);
-					output.startElement("", "", "category", categoryAtts);
-					output.endElement("", "", "category");
-				}
-				
-				output.endElement("", "", "feature");
-			}
-		}
-	}
-
-	public void startPrefixMapping(String prefix, String uri) throws SAXException {
-		output.startPrefixMapping(prefix, uri);
-	}
+
+class SiteInsertFeatureContentHandler implements ContentHandler
+{
+
+    private final ContentHandler output;
+    private final List<org.apache.felix.sigil.build.Feature> featureList;
+
+
+    public SiteInsertFeatureContentHandler( ContentHandler output, List<Feature> featureList )
+    {
+        this.output = output;
+        this.featureList = featureList;
+    }
+
+
+    public void characters( char[] ch, int start, int length ) throws SAXException
+    {
+        output.characters( ch, start, length );
+    }
+
+
+    public void endDocument() throws SAXException
+    {
+        output.endDocument();
+    }
+
+
+    public void endElement( String uri, String localName, String name ) throws SAXException
+    {
+        output.endElement( uri, localName, name );
+    }
+
+
+    public void endPrefixMapping( String prefix ) throws SAXException
+    {
+        output.endPrefixMapping( prefix );
+    }
+
+
+    public void ignorableWhitespace( char[] ch, int start, int length ) throws SAXException
+    {
+        //output.ignorableWhitespace(ch, start, length);
+    }
+
+
+    public void processingInstruction( String target, String data ) throws SAXException
+    {
+        output.processingInstruction( target, data );
+    }
+
+
+    public void setDocumentLocator( Locator locator )
+    {
+        output.setDocumentLocator( locator );
+    }
+
+
+    public void skippedEntity( String name ) throws SAXException
+    {
+        output.skippedEntity( name );
+    }
+
+
+    public void startDocument() throws SAXException
+    {
+        output.startDocument();
+    }
+
+
+    public void startElement( String uri, String localName, String name, Attributes atts ) throws SAXException
+    {
+        output.startElement( uri, localName, name, atts );
+
+        if ( "site".equals( name ) )
+        {
+            for ( Feature feature : featureList )
+            {
+                AttributesImpl featureAtts = new AttributesImpl();
+                featureAtts.addAttribute( "", "", "url", "CDATA", feature.url );
+                featureAtts.addAttribute( "", "", "id", "CDATA", feature.id );
+                featureAtts.addAttribute( "", "", "version", "CDATA", feature.version );
+                output.startElement( "", "", "feature", featureAtts );
+
+                for ( int i = 0; i < feature.categories.length; i++ )
+                {
+                    AttributesImpl categoryAtts = new AttributesImpl();
+                    categoryAtts.addAttribute( "", "", "name", "CDATA", feature.categories[i] );
+                    output.startElement( "", "", "category", categoryAtts );
+                    output.endElement( "", "", "category" );
+                }
+
+                output.endElement( "", "", "feature" );
+            }
+        }
+    }
+
+
+    public void startPrefixMapping( String prefix, String uri ) throws SAXException
+    {
+        output.startPrefixMapping( prefix, uri );
+    }
 
 }

Modified: felix/trunk/sigil/sigil-builder/src/org/apache/felix/sigil/build/SiteInsertFeatures.java
URL: http://svn.apache.org/viewvc/felix/trunk/sigil/sigil-builder/src/org/apache/felix/sigil/build/SiteInsertFeatures.java?rev=796467&r1=796466&r2=796467&view=diff
==============================================================================
--- felix/trunk/sigil/sigil-builder/src/org/apache/felix/sigil/build/SiteInsertFeatures.java (original)
+++ felix/trunk/sigil/sigil-builder/src/org/apache/felix/sigil/build/SiteInsertFeatures.java Tue Jul 21 18:51:33 2009
@@ -18,6 +18,7 @@
  */
 package org.apache.felix.sigil.build;
 
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
@@ -42,138 +43,210 @@
 import org.xml.sax.SAXException;
 import org.xml.sax.XMLReader;
 
-public class SiteInsertFeatures extends Task {
 
-	private File siteXmlFile;
-	private String features;
-	private String versionPropPrefix;
-	private String categoryPropPrefix;
-	
-	public File getSiteXmlFile() {
-		return siteXmlFile;
-	}
-	public void setSiteXmlFile(File siteXmlFile) {
-		this.siteXmlFile = siteXmlFile;
-	}
-	public String getFeatures() {
-		return features;
-	}
-	public void setFeatures(String features) {
-		this.features = features;
-	}
-	public String getVersionPropPrefix() {
-		return versionPropPrefix;
-	}
-	public void setVersionPropPrefix(String versionPropPrefix) {
-		this.versionPropPrefix = versionPropPrefix;
-	}
-	public String getCategoryPropPrefix() {
-		return categoryPropPrefix;
-	}
-	public void setCategoryPropPrefix(String categoryPropPrefix) {
-		this.categoryPropPrefix = categoryPropPrefix;
-	}
-	
-	@Override
-	public void execute() throws BuildException {
-		Project project = getProject();
-		
-		List<Feature> featureList = new ArrayList<Feature>(); 
-		StringTokenizer tokenizer = new StringTokenizer(features, ",");
-		while(tokenizer.hasMoreTokens()) {
-			Feature feature = new Feature();
-			feature.id = tokenizer.nextToken().trim();
-			
-			// Find the version property
-			String versionProp;
-			if(versionPropPrefix == null) {
-				versionProp = feature.id;
-			} else {
-				versionProp = versionPropPrefix + "." + feature.id;
-			}
-			feature.version = project.getProperty(versionProp);
-			
-			// Find the categories for this feature
-			feature.categories = new String[0];
-			if(categoryPropPrefix != null) {
-				String categoriesStr = project.getProperty(categoryPropPrefix + "." + feature.id);
-				if(categoriesStr != null) {
-					StringTokenizer categoriesTokenizer = new StringTokenizer(categoriesStr, ",");
-					feature.categories = new String[categoriesTokenizer.countTokens()];
-					for(int i=0; i<feature.categories.length; i++) {
-						feature.categories[i] = categoriesTokenizer.nextToken();
-					}
-				}
-			}
-
-			if(feature.version != null) {
-				feature.url = "features/" + feature.id + "_" + feature.version + ".jar";
-				featureList.add(feature);
-			} else {
-				System.out.println("Skipping feature " + feature.id);
-			}
-		}
-		
-		if(!siteXmlFile.isFile()) {
-			throw new BuildException(siteXmlFile + " does not exist or is not a normal file");
-		}
-		try {
-			// Generate new XML into a temporary file
-			File tempFile = File.createTempFile("tmp", ".xml", siteXmlFile.getParentFile());
-			tempFile.deleteOnExit();
-
-			SAXTransformerFactory transformerFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
-			TransformerHandler transformerHandler = transformerFactory.newTransformerHandler();
-			transformerHandler.setResult(new StreamResult(tempFile));
-			
-			SAXParserFactory parserFactory = SAXParserFactory.newInstance();
-			SAXParser parser = parserFactory.newSAXParser();
-			
-			SiteInsertFeatureContentHandler contentHandler = new SiteInsertFeatureContentHandler(transformerHandler, featureList);
-			
-			XMLReader reader = parser.getXMLReader();
-			reader.setContentHandler(contentHandler);
-			reader.parse(new InputSource(new FileInputStream(siteXmlFile)));
-			
-			// Backup original file
-			File backup = new File(siteXmlFile.getParentFile(), siteXmlFile.getName() + ".bak");
-			copyFile(siteXmlFile, backup);
-			
-			// Replace original file
-			copyFile(tempFile, siteXmlFile);
-			
-		} catch (IOException e) {
-			throw new BuildException(e);
-		} catch (TransformerConfigurationException e) {
-			throw new BuildException(e);
-		} catch (IllegalArgumentException e) {
-			throw new BuildException(e);
-		} catch (TransformerFactoryConfigurationError e) {
-			throw new BuildException(e);
-		} catch (ParserConfigurationException e) {
-			throw new BuildException(e);
-		} catch (SAXException e) {
-			throw new BuildException(e);
-		}
-	}
-	
-	private void copyFile(File source, File dest) throws IOException {
-		FileInputStream in = null;
-		FileOutputStream out = null;
-		try {
-			in = new FileInputStream(source);
-			out = new FileOutputStream(dest);
-			
-			byte[] buffer = new byte[1024];
-			
-			int read;
-			while((read = in.read(buffer, 0, 1024)) > -1) {
-				out.write(buffer, 0, read);
-			}
-		} finally {
-			try { if(in != null) in.close(); } catch(IOException e) {}
-			try { if(out != null) out.close(); } catch(IOException e) {}
-		}
-		
-	}
+public class SiteInsertFeatures extends Task
+{
+
+    private File siteXmlFile;
+    private String features;
+    private String versionPropPrefix;
+    private String categoryPropPrefix;
+
+
+    public File getSiteXmlFile()
+    {
+        return siteXmlFile;
+    }
+
+
+    public void setSiteXmlFile( File siteXmlFile )
+    {
+        this.siteXmlFile = siteXmlFile;
+    }
+
+
+    public String getFeatures()
+    {
+        return features;
+    }
+
+
+    public void setFeatures( String features )
+    {
+        this.features = features;
+    }
+
+
+    public String getVersionPropPrefix()
+    {
+        return versionPropPrefix;
+    }
+
+
+    public void setVersionPropPrefix( String versionPropPrefix )
+    {
+        this.versionPropPrefix = versionPropPrefix;
+    }
+
+
+    public String getCategoryPropPrefix()
+    {
+        return categoryPropPrefix;
+    }
+
+
+    public void setCategoryPropPrefix( String categoryPropPrefix )
+    {
+        this.categoryPropPrefix = categoryPropPrefix;
+    }
+
+
+    @Override
+    public void execute() throws BuildException
+    {
+        Project project = getProject();
+
+        List<Feature> featureList = new ArrayList<Feature>();
+        StringTokenizer tokenizer = new StringTokenizer( features, "," );
+        while ( tokenizer.hasMoreTokens() )
+        {
+            Feature feature = new Feature();
+            feature.id = tokenizer.nextToken().trim();
+
+            // Find the version property
+            String versionProp;
+            if ( versionPropPrefix == null )
+            {
+                versionProp = feature.id;
+            }
+            else
+            {
+                versionProp = versionPropPrefix + "." + feature.id;
+            }
+            feature.version = project.getProperty( versionProp );
+
+            // Find the categories for this feature
+            feature.categories = new String[0];
+            if ( categoryPropPrefix != null )
+            {
+                String categoriesStr = project.getProperty( categoryPropPrefix + "." + feature.id );
+                if ( categoriesStr != null )
+                {
+                    StringTokenizer categoriesTokenizer = new StringTokenizer( categoriesStr, "," );
+                    feature.categories = new String[categoriesTokenizer.countTokens()];
+                    for ( int i = 0; i < feature.categories.length; i++ )
+                    {
+                        feature.categories[i] = categoriesTokenizer.nextToken();
+                    }
+                }
+            }
+
+            if ( feature.version != null )
+            {
+                feature.url = "features/" + feature.id + "_" + feature.version + ".jar";
+                featureList.add( feature );
+            }
+            else
+            {
+                System.out.println( "Skipping feature " + feature.id );
+            }
+        }
+
+        if ( !siteXmlFile.isFile() )
+        {
+            throw new BuildException( siteXmlFile + " does not exist or is not a normal file" );
+        }
+        try
+        {
+            // Generate new XML into a temporary file
+            File tempFile = File.createTempFile( "tmp", ".xml", siteXmlFile.getParentFile() );
+            tempFile.deleteOnExit();
+
+            SAXTransformerFactory transformerFactory = ( SAXTransformerFactory ) SAXTransformerFactory.newInstance();
+            TransformerHandler transformerHandler = transformerFactory.newTransformerHandler();
+            transformerHandler.setResult( new StreamResult( tempFile ) );
+
+            SAXParserFactory parserFactory = SAXParserFactory.newInstance();
+            SAXParser parser = parserFactory.newSAXParser();
+
+            SiteInsertFeatureContentHandler contentHandler = new SiteInsertFeatureContentHandler( transformerHandler,
+                featureList );
+
+            XMLReader reader = parser.getXMLReader();
+            reader.setContentHandler( contentHandler );
+            reader.parse( new InputSource( new FileInputStream( siteXmlFile ) ) );
+
+            // Backup original file
+            File backup = new File( siteXmlFile.getParentFile(), siteXmlFile.getName() + ".bak" );
+            copyFile( siteXmlFile, backup );
+
+            // Replace original file
+            copyFile( tempFile, siteXmlFile );
+
+        }
+        catch ( IOException e )
+        {
+            throw new BuildException( e );
+        }
+        catch ( TransformerConfigurationException e )
+        {
+            throw new BuildException( e );
+        }
+        catch ( IllegalArgumentException e )
+        {
+            throw new BuildException( e );
+        }
+        catch ( TransformerFactoryConfigurationError e )
+        {
+            throw new BuildException( e );
+        }
+        catch ( ParserConfigurationException e )
+        {
+            throw new BuildException( e );
+        }
+        catch ( SAXException e )
+        {
+            throw new BuildException( e );
+        }
+    }
+
+
+    private void copyFile( File source, File dest ) throws IOException
+    {
+        FileInputStream in = null;
+        FileOutputStream out = null;
+        try
+        {
+            in = new FileInputStream( source );
+            out = new FileOutputStream( dest );
+
+            byte[] buffer = new byte[1024];
+
+            int read;
+            while ( ( read = in.read( buffer, 0, 1024 ) ) > -1 )
+            {
+                out.write( buffer, 0, read );
+            }
+        }
+        finally
+        {
+            try
+            {
+                if ( in != null )
+                    in.close();
+            }
+            catch ( IOException e )
+            {
+            }
+            try
+            {
+                if ( out != null )
+                    out.close();
+            }
+            catch ( IOException e )
+            {
+            }
+        }
+
+    }
 }