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.io.BufferedReader;
23 import java.io.ByteArrayInputStream;
24 import java.io.ByteArrayOutputStream;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.InputStreamReader;
28 import java.net.URL;
29 import java.util.HashSet;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34 import java.util.TreeSet;
35 import java.util.regex.Pattern;
36
37 import javax.xml.transform.Transformer;
38 import javax.xml.transform.TransformerFactory;
39 import javax.xml.transform.stream.StreamResult;
40 import javax.xml.transform.stream.StreamSource;
41
42 import aQute.bnd.service.AnalyzerPlugin;
43 import aQute.bnd.osgi.Analyzer;
44 import aQute.bnd.osgi.Descriptors.PackageRef;
45 import aQute.bnd.osgi.Jar;
46 import aQute.bnd.osgi.Processor;
47 import aQute.bnd.osgi.Resource;
48 import aQute.libg.generics.Create;
49 import aQute.libg.qtokens.QuotedTokenizer;
50 import aQute.service.reporter.Reporter;
51
52
53 public class BlueprintPlugin implements AnalyzerPlugin
54 {
55
56 static Pattern QN = Pattern.compile( "[_A-Za-z$][_A-Za-z0-9$]*(\\.[_A-Za-z$][_A-Za-z0-9$]*)*" );
57 static Pattern PATHS = Pattern.compile( ".*\\.xml" );
58
59 Transformer transformer;
60
61
62 public BlueprintPlugin() throws Exception
63 {
64 transformer = getTransformer( getClass().getResource( "blueprint.xsl" ) );
65 }
66
67
68 public boolean analyzeJar( Analyzer analyzer ) throws Exception
69 {
70 transformer.setParameter( "nsh_interface",
71 analyzer.getProperty( "nsh_interface" ) != null ? analyzer.getProperty( "nsh_interface" ) : "" );
72 transformer.setParameter( "nsh_namespace",
73 analyzer.getProperty( "nsh_namespace" ) != null ? analyzer.getProperty( "nsh_namespace" ) : "" );
74
75 Set<String> headers = Create.set();
76
77 String bpHeader = analyzer.getProperty( "Bundle-Blueprint", "OSGI-INF/blueprint" );
78 Map<String, ? extends Map<String, String>> map = Processor.parseHeader( bpHeader, null );
79 bpHeader = "";
80 for ( String root : map.keySet() )
81 {
82 Jar jar = analyzer.getJar();
83 Map<String, Resource> dir = jar.getDirectories().get( root );
84 if ( dir == null || dir.isEmpty() )
85 {
86 Resource resource = jar.getResource( root );
87 if ( resource != null )
88 {
89 process( analyzer, root, resource, headers );
90 if (bpHeader.length() > 0) {
91 bpHeader += ",";
92 }
93 bpHeader += root;
94 }
95 continue;
96 }
97 for ( Map.Entry<String, Resource> entry : dir.entrySet() )
98 {
99 String path = entry.getKey();
100 Resource resource = entry.getValue();
101 if ( PATHS.matcher( path ).matches() )
102 {
103 process( analyzer, path, resource, headers );
104 if (bpHeader.length() > 0) {
105 bpHeader += ",";
106 }
107 bpHeader += path;
108 }
109 }
110 }
111 if( !map.isEmpty() )
112 {
113 analyzer.setProperty("Bundle-Blueprint", bpHeader);
114 }
115
116
117 Map<String, Set<Attribute>> hdrs = Create.map();
118 for ( String str : headers )
119 {
120 int idx = str.indexOf( ':' );
121 if ( idx < 0 )
122 {
123 analyzer.warning( ( new StringBuilder( "Error analyzing services in blueprint resource: " ) ).append(
124 str ).toString() );
125 continue;
126 }
127 String h = str.substring( 0, idx ).trim();
128 String v = str.substring( idx + 1 ).trim();
129 Set<Attribute> att = hdrs.get( h );
130 if ( att == null )
131 {
132 att = new TreeSet<Attribute>();
133 hdrs.put( h, att );
134 }
135 att.addAll( parseHeader( v, null ) );
136 }
137
138 for ( String header : hdrs.keySet() )
139 {
140 if ( "Import-Class".equals( header ) || "Import-Package".equals( header ) )
141 {
142 Set<Attribute> newAttr = hdrs.get( header );
143 for ( Attribute a : newAttr )
144 {
145 String pkg = a.getName();
146 if ( "Import-Class".equals( header ) )
147 {
148 int n = a.getName().lastIndexOf( '.' );
149 if ( n > 0 )
150 {
151 pkg = pkg.subSequence( 0, n ).toString();
152 }
153 else
154 {
155 continue;
156 }
157 }
158 PackageRef pkgRef = analyzer.getPackageRef( pkg );
159 if ( !analyzer.getReferred().containsKey( pkgRef ) )
160 {
161 analyzer.getReferred().put( pkgRef ).putAll( a.getProperties() );
162 }
163 }
164 }
165 else
166 {
167 Set<Attribute> orgAttr = parseHeader( analyzer.getProperty( header ), null );
168 Set<Attribute> newAttr = hdrs.get( header );
169 for ( Iterator<Attribute> it = newAttr.iterator(); it.hasNext(); )
170 {
171 Attribute a = it.next();
172 for ( Attribute b : orgAttr )
173 {
174 if ( b.getName().equals( a.getName() ) )
175 {
176 it.remove();
177 break;
178 }
179 }
180 }
181 orgAttr.addAll( newAttr );
182
183 StringBuilder sb = new StringBuilder();
184 for ( Attribute a : orgAttr )
185 {
186 if ( sb.length() > 0 )
187 {
188 sb.append( "," );
189 }
190 sb.append( a.getName() );
191 for ( Map.Entry<String, String> prop : a.getProperties().entrySet() )
192 {
193 sb.append( ';' ).append( prop.getKey() ).append( "=" );
194 if ( prop.getValue().matches( "[0-9a-zA-Z_-]+" ) )
195 {
196 sb.append( prop.getValue() );
197 }
198 else
199 {
200 sb.append( "\"" );
201 sb.append( prop.getValue().replace( "\"", "\\\"" ) );
202 sb.append( "\"" );
203 }
204 }
205 }
206 analyzer.setProperty( header, sb.toString() );
207 }
208 }
209 return false;
210 }
211
212
213 private void process( Analyzer analyzer, String path, Resource resource, Set<String> headers )
214 {
215 InputStream in = null;
216 try
217 {
218 in = resource.openInputStream();
219
220
221 Set<String> set = analyze( in );
222 headers.addAll( set );
223 }
224 catch ( Exception e )
225 {
226 analyzer.error( ( new StringBuilder( "Unexpected exception in processing spring resources(" ) )
227 .append( path ).append( "): " ).append( e ).toString() );
228 }
229 finally
230 {
231 try
232 {
233 if ( in != null )
234 {
235 in.close();
236 }
237 }
238 catch ( IOException e )
239 {
240 }
241 }
242 }
243
244
245 public Set<String> analyze( InputStream in ) throws Exception
246 {
247 Set<String> refers = new HashSet<String>();
248 ByteArrayOutputStream bout = new ByteArrayOutputStream();
249 javax.xml.transform.Result r = new StreamResult( bout );
250 javax.xml.transform.Source s = new StreamSource( in );
251 transformer.transform( s, r );
252 ByteArrayInputStream bin = new ByteArrayInputStream( bout.toByteArray() );
253 bout.close();
254 BufferedReader br = new BufferedReader( new InputStreamReader( bin ) );
255 for ( String line = br.readLine(); line != null; line = br.readLine() )
256 {
257 line = line.trim();
258 line = line.replace( ";availability:=mandatory", "" );
259 if ( line.length() > 0 )
260 {
261 refers.add( line );
262 }
263 }
264
265 br.close();
266 return refers;
267 }
268
269
270 protected Transformer getTransformer( URL url ) throws Exception
271 {
272 TransformerFactory tf = TransformerFactory.newInstance();
273 javax.xml.transform.Source source = new StreamSource( url.openStream() );
274 return tf.newTransformer( source );
275 }
276
277 public static class Attribute implements Comparable<Attribute>
278 {
279 private final String name;
280 private final Map<String, String> properties;
281
282
283 public Attribute( String name, Map<String, String> properties )
284 {
285 this.name = name;
286 this.properties = properties;
287 }
288
289
290 public String getName()
291 {
292 return name;
293 }
294
295
296 public Map<String, String> getProperties()
297 {
298 return properties;
299 }
300
301
302 public int compareTo( Attribute a )
303 {
304 int c = name.compareTo( a.name );
305 if ( c == 0 )
306 {
307 c = properties.equals( a.properties ) ? 0 : properties.size() < a.properties.size() ? -1 : properties
308 .hashCode() < a.properties.hashCode() ? -1 : +1;
309 }
310 return c;
311 }
312
313
314 @Override
315 public boolean equals( Object o )
316 {
317 if ( this == o )
318 return true;
319 if ( o == null || getClass() != o.getClass() )
320 return false;
321
322 Attribute attribute = ( Attribute ) o;
323
324 if ( name != null ? !name.equals( attribute.name ) : attribute.name != null )
325 return false;
326 if ( properties != null ? !properties.equals( attribute.properties ) : attribute.properties != null )
327 return false;
328
329 return true;
330 }
331
332
333 @Override
334 public int hashCode()
335 {
336 int result = name != null ? name.hashCode() : 0;
337 result = 31 * result + ( properties != null ? properties.hashCode() : 0 );
338 return result;
339 }
340 }
341
342
343 public static Set<Attribute> parseHeader( String value, Reporter logger )
344 {
345 if ( ( value == null ) || ( value.trim().length() == 0 ) )
346 {
347 return new TreeSet<Attribute>();
348 }
349 Set<Attribute> result = new TreeSet<Attribute>();
350 QuotedTokenizer qt = new QuotedTokenizer( value, ";=," );
351 char del = '\0';
352 do
353 {
354 boolean hadAttribute = false;
355 Map<String, String> clause = Create.map();
356 List<String> aliases = Create.list();
357 String name = qt.nextToken( ",;" );
358
359 del = qt.getSeparator();
360 if ( ( name == null ) || ( name.length() == 0 ) )
361 {
362 if ( ( logger != null ) && ( logger.isPedantic() ) )
363 {
364 logger
365 .warning( "Empty clause, usually caused by repeating a comma without any name field or by having "
366 + "spaces after the backslash of a property file: " + value );
367 }
368
369 if ( name != null )
370 continue;
371 break;
372 }
373 name = name.trim();
374
375 aliases.add( name );
376 String advalue;
377 while ( del == ';' )
378 {
379 String adname = qt.nextToken();
380 if ( ( del = qt.getSeparator() ) != '=' )
381 {
382 if ( ( hadAttribute ) && ( logger != null ) )
383 {
384 logger.error( "Header contains name field after attribute or directive: " + adname + " from "
385 + value + ". Name fields must be consecutive, separated by a ';' like a;b;c;x=3;y=4" );
386 }
387
388 if ( ( adname != null ) && ( adname.length() > 0 ) )
389 aliases.add( adname.trim() );
390 }
391 else
392 {
393 advalue = qt.nextToken();
394 if ( ( clause.containsKey( adname ) ) && ( logger != null ) && ( logger.isPedantic() ) )
395 {
396 logger.warning( "Duplicate attribute/directive name " + adname + " in " + value
397 + ". This attribute/directive will be ignored" );
398 }
399
400 if ( advalue == null )
401 {
402 if ( logger != null )
403 {
404 logger.error( "No value after '=' sign for attribute " + adname );
405 }
406 advalue = "";
407 }
408 clause.put( adname.trim(), advalue.trim() );
409 del = qt.getSeparator();
410 hadAttribute = true;
411 }
412 }
413
414 for ( String clauseName : aliases )
415 {
416 result.add( new Attribute( clauseName, clause ) );
417 }
418 }
419 while ( del == ',' );
420 return result;
421 }
422
423 }