| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 org.apache.felix.bundleplugin; |
| |
| |
| import java.util.Collection; |
| import java.util.Iterator; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.regex.Pattern; |
| |
| import org.apache.maven.artifact.Artifact; |
| import org.apache.maven.artifact.resolver.filter.AndArtifactFilter; |
| import org.apache.maven.artifact.resolver.filter.ArtifactFilter; |
| import org.apache.maven.plugin.MojoExecutionException; |
| |
| import aQute.bnd.header.Attrs; |
| import aQute.bnd.header.OSGiHeader; |
| import aQute.bnd.osgi.Instruction; |
| import org.apache.maven.shared.dependency.graph.DependencyNode; |
| import org.apache.maven.shared.dependency.graph.filter.ArtifactDependencyNodeFilter; |
| import org.apache.maven.shared.dependency.graph.filter.DependencyNodeFilter; |
| import org.apache.maven.shared.dependency.graph.traversal.BuildingDependencyNodeVisitor; |
| import org.apache.maven.shared.dependency.graph.traversal.CollectingDependencyNodeVisitor; |
| import org.apache.maven.shared.dependency.graph.traversal.DependencyNodeVisitor; |
| import org.apache.maven.shared.dependency.graph.traversal.FilteringDependencyNodeVisitor; |
| |
| |
| /** |
| * Apply clause-based filter over given dependencies |
| * |
| * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> |
| */ |
| public abstract class AbstractDependencyFilter |
| { |
| private static final Pattern MISSING_KEY_PATTERN = Pattern.compile( "(^|,)\\p{Blank}*(!)?\\p{Blank}*([a-zA-Z]+=)" ); |
| |
| /** |
| * Dependency Graph. |
| */ |
| private final DependencyNode m_dependencyGraph; |
| /** |
| * Dependency artifacts. |
| */ |
| private final Collection<Artifact> m_dependencyArtifacts; |
| |
| |
| public AbstractDependencyFilter( DependencyNode dependencyGraph, Collection<Artifact> dependencyArtifacts ) |
| { |
| m_dependencyGraph = dependencyGraph; |
| m_dependencyArtifacts = dependencyArtifacts; |
| } |
| |
| private static abstract class DependencyFilter implements ArtifactFilter |
| { |
| private final Instruction m_instruction; |
| private final String m_defaultValue; |
| |
| |
| public DependencyFilter( String expression ) |
| { |
| this( expression, "" ); |
| } |
| |
| |
| public DependencyFilter( String expression, String defaultValue ) |
| { |
| m_instruction = new Instruction( expression ); |
| m_defaultValue = defaultValue; |
| } |
| |
| public abstract boolean include( Artifact dependency ); |
| |
| boolean matches( String text ) |
| { |
| boolean result; |
| |
| if ( null == text ) |
| { |
| result = m_instruction.matches( m_defaultValue ); |
| } |
| else |
| { |
| result = m_instruction.matches( text ); |
| } |
| |
| return m_instruction.isNegated() ? !result : result; |
| } |
| } |
| |
| private static class TrimmingDependencyNodeFilter implements DependencyNodeFilter |
| { |
| private DependencyNodeFilter dependencyNodeFilter; |
| |
| public TrimmingDependencyNodeFilter( DependencyNodeFilter dependencyNodeFilter ) |
| { |
| this.dependencyNodeFilter = dependencyNodeFilter; |
| } |
| |
| public boolean accept( DependencyNode node ) |
| { |
| boolean accepted = dependencyNodeFilter.accept( node ); |
| if( !accepted ) |
| { |
| List<DependencyNode> children = node.getChildren(); |
| children.clear(); |
| } |
| return accepted; |
| } |
| } |
| |
| protected final void processInstructions( String header ) throws MojoExecutionException |
| { |
| Map<String,Attrs> instructions = OSGiHeader.parseHeader( MISSING_KEY_PATTERN.matcher( header ).replaceAll( "$1$2*;$3" ) ); |
| |
| Collection<Artifact> availableDependencies = new LinkedHashSet<Artifact>( m_dependencyArtifacts ); |
| |
| for ( Iterator<Map.Entry<String,Attrs>> clauseIterator = instructions.entrySet().iterator(); clauseIterator.hasNext(); ) |
| { |
| String inline = "false"; |
| |
| // always start with a fresh *modifiable* collection for each unique clause |
| Collection<Artifact> filteredDependencies = new LinkedHashSet<Artifact>( availableDependencies ); |
| |
| // CLAUSE: REGEXP --> { ATTRIBUTE MAP } |
| Map.Entry<String,Attrs> clause = clauseIterator.next(); |
| String primaryKey = clause.getKey().replaceFirst( "~+$", "" ); |
| boolean isNegative = primaryKey.startsWith( "!" ); |
| if ( isNegative ) |
| { |
| primaryKey = primaryKey.substring( 1 ); |
| } |
| |
| final AndArtifactFilter andArtifactFilter = new AndArtifactFilter(); |
| if ( !"*".equals( primaryKey ) ) |
| { |
| ArtifactFilter filter = new DependencyFilter( primaryKey ) |
| { |
| @Override |
| public boolean include( Artifact dependency ) |
| { |
| return super.matches( dependency.getArtifactId() ); |
| } |
| }; |
| // FILTER ON MAIN CLAUSE |
| andArtifactFilter.add(filter); |
| } |
| |
| for ( Iterator<Map.Entry<String,String>> attrIterator = clause.getValue().entrySet().iterator(); attrIterator.hasNext(); ) |
| { |
| final ArtifactFilter filter; |
| // ATTRIBUTE: KEY --> REGEXP |
| Map.Entry<String,String> attr = attrIterator.next(); |
| if ( "groupId".equals( attr.getKey() ) ) |
| { |
| filter = new DependencyFilter( attr.getValue() ) |
| { |
| @Override |
| public boolean include( Artifact dependency ) |
| { |
| return super.matches( dependency.getGroupId() ); |
| } |
| }; |
| } |
| else if ( "artifactId".equals( attr.getKey() ) ) |
| { |
| filter = new DependencyFilter( attr.getValue() ) |
| { |
| @Override |
| public boolean include( Artifact dependency ) |
| { |
| return super.matches( dependency.getArtifactId() ); |
| } |
| }; |
| } |
| else if ( "version".equals( attr.getKey() ) ) |
| { |
| filter = new DependencyFilter( attr.getValue() ) |
| { |
| @Override |
| public boolean include( Artifact dependency ) |
| { |
| try |
| { |
| // use the symbolic version if available (ie. 1.0.0-SNAPSHOT) |
| return super.matches( dependency.getSelectedVersion().toString() ); |
| } |
| catch ( Exception e ) |
| { |
| return super.matches( dependency.getVersion() ); |
| } |
| } |
| }; |
| } |
| else if ( "scope".equals( attr.getKey() ) ) |
| { |
| filter = new DependencyFilter( attr.getValue(), "compile" ) |
| { |
| @Override |
| public boolean include( Artifact dependency ) |
| { |
| return super.matches( dependency.getScope() ); |
| } |
| }; |
| } |
| else if ( "type".equals( attr.getKey() ) ) |
| { |
| filter = new DependencyFilter( attr.getValue(), "jar" ) |
| { |
| @Override |
| public boolean include( Artifact dependency ) |
| { |
| return super.matches( dependency.getType() ); |
| } |
| }; |
| } |
| else if ( "classifier".equals( attr.getKey() ) ) |
| { |
| filter = new DependencyFilter( attr.getValue() ) |
| { |
| @Override |
| public boolean include( Artifact dependency ) |
| { |
| return super.matches( dependency.getClassifier() ); |
| } |
| }; |
| } |
| else if ( "optional".equals( attr.getKey() ) ) |
| { |
| filter = new DependencyFilter( attr.getValue(), "false" ) |
| { |
| @Override |
| public boolean include( Artifact dependency ) |
| { |
| return super.matches( "" + dependency.isOptional() ); |
| } |
| }; |
| } |
| else if ( "inline".equals( attr.getKey() ) ) |
| { |
| inline = attr.getValue(); |
| continue; |
| } |
| else |
| { |
| throw new MojoExecutionException( "Unexpected attribute " + attr.getKey() ); |
| } |
| |
| // FILTER ON EACH ATTRIBUTE |
| andArtifactFilter.add( filter ); |
| } |
| |
| filteredDependencies( andArtifactFilter, filteredDependencies ); |
| |
| if ( isNegative ) |
| { |
| // negative clauses reduce the set of available artifacts |
| availableDependencies.removeAll( filteredDependencies ); |
| if ( !clauseIterator.hasNext() ) |
| { |
| // assume there's an implicit * missing at the end |
| processDependencies( availableDependencies, inline ); |
| } |
| } |
| else |
| { |
| // positive clause; doesn't alter the available artifacts |
| processDependencies( filteredDependencies, inline ); |
| } |
| } |
| } |
| |
| |
| protected abstract void processDependencies( Collection<Artifact> dependencies, String inline ); |
| |
| private void filteredDependencies( final ArtifactFilter artifactFilter, Collection<Artifact> filteredDependencies ) |
| { |
| CollectingDependencyNodeVisitor collectingDependencyNodeVisitor = new CollectingDependencyNodeVisitor(); |
| final Artifact rootArtifact = m_dependencyGraph.getArtifact(); |
| ArtifactFilter filter = new ArtifactFilter() |
| { |
| |
| |
| public boolean include( Artifact artifact ) |
| { |
| return artifact.equals( rootArtifact ) || artifactFilter.include( artifact ); |
| } |
| |
| |
| }; |
| DependencyNodeFilter dependencyNodeFilter = new ArtifactDependencyNodeFilter( filter ); |
| dependencyNodeFilter = new TrimmingDependencyNodeFilter( dependencyNodeFilter ); |
| DependencyNodeVisitor dependencyNodeVisitor = |
| new FilteringDependencyNodeVisitor( collectingDependencyNodeVisitor, dependencyNodeFilter ); |
| dependencyNodeVisitor = new BuildingDependencyNodeVisitor( dependencyNodeVisitor ); |
| m_dependencyGraph.accept( dependencyNodeVisitor ); |
| List<DependencyNode> dependencyNodes = collectingDependencyNodeVisitor.getNodes(); |
| Set<String> ids = new LinkedHashSet<String>( dependencyNodes.size() ); |
| for( DependencyNode dependencyNode : dependencyNodes ) { |
| Artifact artifact = dependencyNode.getArtifact(); |
| String id = artifact.getId(); |
| ids.add(id); |
| } |
| for (Iterator<Artifact> iterator = filteredDependencies.iterator(); iterator.hasNext();) |
| { |
| Artifact artifact = iterator.next(); |
| String id = artifact.getId(); |
| if (!ids.contains(id)) |
| { |
| iterator.remove(); |
| } |
| } |
| } |
| } |