1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.felix.bundleplugin;
20
21
22 import java.util.Collection;
23 import java.util.Iterator;
24 import java.util.LinkedHashSet;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.regex.Pattern;
28
29 import org.apache.maven.artifact.Artifact;
30 import org.apache.maven.artifact.resolver.filter.AndArtifactFilter;
31 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
32 import org.apache.maven.plugin.MojoExecutionException;
33
34 import aQute.bnd.header.Attrs;
35 import aQute.bnd.header.OSGiHeader;
36 import aQute.bnd.osgi.Instruction;
37
38
39
40
41
42
43
44 public abstract class AbstractDependencyFilter
45 {
46 private static final Pattern MISSING_KEY_PATTERN = Pattern.compile( "(^|,)\\p{Blank}*(!)?\\p{Blank}*([a-zA-Z]+=)" );
47 private static final String PLACEHOLDER = "$$PLACEHOLDER$$";
48
49
50
51
52 private final Collection<Artifact> m_dependencyArtifacts;
53
54
55 public AbstractDependencyFilter( Collection<Artifact> dependencyArtifacts )
56 {
57 m_dependencyArtifacts = dependencyArtifacts;
58 }
59
60 private static abstract class DependencyFilter implements ArtifactFilter
61 {
62 private final Instruction m_instruction;
63 private final String m_defaultValue;
64
65
66 public DependencyFilter( String expression )
67 {
68 this( expression, "" );
69 }
70
71
72 public DependencyFilter( String expression, String defaultValue )
73 {
74 m_instruction = new Instruction( expression );
75 m_defaultValue = defaultValue;
76 }
77
78 public abstract boolean include( Artifact dependency );
79
80 boolean matches( String text )
81 {
82 boolean result;
83
84 if ( null == text )
85 {
86 result = m_instruction.matches( m_defaultValue );
87 }
88 else
89 {
90 result = m_instruction.matches( text );
91 }
92
93 return m_instruction.isNegated() ? !result : result;
94 }
95 }
96
97 protected final void processInstructions( String header ) throws MojoExecutionException
98 {
99 Map<String,Attrs> instructions = OSGiHeader.parseHeader( MISSING_KEY_PATTERN.matcher( header ).replaceAll( "$1$2*;$3" ) );
100
101 Collection<Artifact> availableDependencies = new LinkedHashSet<Artifact>( m_dependencyArtifacts );
102
103 for ( Iterator<Map.Entry<String,Attrs>> clauseIterator = instructions.entrySet().iterator(); clauseIterator.hasNext(); )
104 {
105 String inline = "false";
106
107
108 Collection<Artifact> filteredDependencies = new LinkedHashSet<Artifact>( availableDependencies );
109
110
111 Map.Entry<String,Attrs> clause = clauseIterator.next();
112 String primaryKey = clause.getKey().replaceFirst( "~+$", "" );
113 boolean isNegative = primaryKey.startsWith( "!" );
114 if ( isNegative )
115 {
116 primaryKey = primaryKey.substring( 1 );
117 }
118
119 final AndArtifactFilter andArtifactFilter = new AndArtifactFilter();
120 if ( !"*".equals( primaryKey ) )
121 {
122 ArtifactFilter filter = new DependencyFilter( primaryKey )
123 {
124 @Override
125 public boolean include( Artifact dependency )
126 {
127 return super.matches( dependency.getArtifactId() );
128 }
129 };
130
131 andArtifactFilter.add(filter);
132 }
133
134 for ( Iterator<Map.Entry<String,String>> attrIterator = clause.getValue().entrySet().iterator(); attrIterator.hasNext(); )
135 {
136 final ArtifactFilter filter;
137
138 Map.Entry<String,String> attr = attrIterator.next();
139 if ( "groupId".equals( attr.getKey() ) )
140 {
141 filter = new DependencyFilter( attr.getValue() )
142 {
143 @Override
144 public boolean include( Artifact dependency )
145 {
146 return super.matches( dependency.getGroupId() );
147 }
148 };
149 }
150 else if ( "artifactId".equals( attr.getKey() ) )
151 {
152 filter = new DependencyFilter( attr.getValue() )
153 {
154 @Override
155 public boolean include( Artifact dependency )
156 {
157 return super.matches( dependency.getArtifactId() );
158 }
159 };
160 }
161 else if ( "version".equals( attr.getKey() ) )
162 {
163 filter = new DependencyFilter( attr.getValue() )
164 {
165 @Override
166 public boolean include( Artifact dependency )
167 {
168 try
169 {
170
171 return super.matches( dependency.getSelectedVersion().toString() );
172 }
173 catch ( Exception e )
174 {
175 return super.matches( dependency.getVersion() );
176 }
177 }
178 };
179 }
180 else if ( "scope".equals( attr.getKey() ) )
181 {
182 filter = new DependencyFilter( attr.getValue(), "compile" )
183 {
184 @Override
185 public boolean include( Artifact dependency )
186 {
187 return super.matches( dependency.getScope() );
188 }
189 };
190 }
191 else if ( "type".equals( attr.getKey() ) )
192 {
193 filter = new DependencyFilter( attr.getValue(), "jar" )
194 {
195 @Override
196 public boolean include( Artifact dependency )
197 {
198 return super.matches( dependency.getType() );
199 }
200 };
201 }
202 else if ( "classifier".equals( attr.getKey() ) )
203 {
204
205
206
207 filter = new DependencyFilter( ( "".equals(attr.getValue()) ) ? PLACEHOLDER : attr.getValue() )
208 {
209 @Override
210 public boolean include( Artifact dependency )
211 {
212 return super.matches( ( ( dependency.getClassifier() == null ) || ( "".equals( dependency.getClassifier() ) ) ) ? PLACEHOLDER : dependency.getClassifier() );
213 }
214 };
215 }
216 else if ( "optional".equals( attr.getKey() ) )
217 {
218 filter = new DependencyFilter( attr.getValue(), "false" )
219 {
220 @Override
221 public boolean include( Artifact dependency )
222 {
223 return super.matches( "" + dependency.isOptional() );
224 }
225 };
226 }
227 else if ( "inline".equals( attr.getKey() ) )
228 {
229 inline = attr.getValue();
230 continue;
231 }
232 else
233 {
234 throw new MojoExecutionException( "Unexpected attribute " + attr.getKey() );
235 }
236
237
238 andArtifactFilter.add( filter );
239 }
240
241 filteredDependencies( andArtifactFilter, filteredDependencies );
242
243 if ( isNegative )
244 {
245
246 availableDependencies.removeAll( filteredDependencies );
247 if ( !clauseIterator.hasNext() )
248 {
249
250 processDependencies( availableDependencies, inline );
251 }
252 }
253 else
254 {
255
256 processDependencies( filteredDependencies, inline );
257 }
258 }
259 }
260
261
262 protected abstract void processDependencies( Collection<Artifact> dependencies, String inline );
263
264 private void filteredDependencies( final ArtifactFilter artifactFilter, Collection<Artifact> filteredDependencies )
265 {
266 filteredDependencies.removeIf( artifact -> !artifactFilter.include( artifact ) );
267 }
268 }