1 package org.apache.maven.report.projectinfo;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.commons.validator.UrlValidator;
23 import org.apache.maven.doxia.sink.Sink;
24 import org.apache.maven.model.License;
25 import org.apache.maven.project.MavenProject;
26 import org.apache.maven.settings.Settings;
27 import org.codehaus.plexus.i18n.I18N;
28 import org.codehaus.plexus.util.StringUtils;
29
30 import java.io.File;
31 import java.io.IOException;
32 import java.net.MalformedURLException;
33 import java.net.URL;
34 import java.util.Iterator;
35 import java.util.List;
36 import java.util.Locale;
37 import java.util.regex.Matcher;
38 import java.util.regex.Pattern;
39
40
41
42
43
44
45
46
47
48 public class LicenseReport
49 extends AbstractProjectInfoReport
50 {
51
52
53
54
55
56
57
58
59
60
61
62 private Settings settings;
63
64
65
66
67
68
69 private boolean offline;
70
71
72
73
74
75
76 public void executeReport( Locale locale )
77 {
78 LicenseRenderer r = new LicenseRenderer( getSink(), getProject(), i18n, locale, settings );
79
80 r.render();
81 }
82
83
84 public boolean canGenerateReport()
85 {
86 if ( !offline )
87 {
88 return true;
89 }
90
91 List licenses = project.getModel().getLicenses();
92 for ( Iterator i = licenses.iterator(); i.hasNext(); )
93 {
94 License license = (License) i.next();
95
96 String url = license.getUrl();
97
98 URL licenseUrl = null;
99 try
100 {
101 licenseUrl = getLicenseURL( project, url );
102 }
103 catch ( MalformedURLException e )
104 {
105 getLog().error( e.getMessage() );
106 }
107 catch ( IOException e )
108 {
109 getLog().error( e.getMessage() );
110 }
111
112 if ( licenseUrl != null && licenseUrl.getProtocol().equals( "file" ) )
113 {
114 return true;
115 }
116 }
117
118 return false;
119 }
120
121
122 public String getOutputName()
123 {
124 return "license";
125 }
126
127 protected String getI18Nsection()
128 {
129 return "license";
130 }
131
132
133
134
135
136
137
138 protected static URL getLicenseURL( MavenProject project, String url )
139 throws IOException
140 {
141 URL licenseUrl = null;
142 UrlValidator urlValidator = new UrlValidator( UrlValidator.ALLOW_ALL_SCHEMES );
143
144
145
146
147 if ( urlValidator.isValid( url ) || StringUtils.defaultString( url ).startsWith( "file://" ) )
148 {
149 try
150 {
151 licenseUrl = new URL( url );
152 }
153 catch ( MalformedURLException e )
154 {
155 throw new MalformedURLException( "The license url '" + url + "' seems to be invalid: "
156 + e.getMessage() );
157 }
158 }
159 else
160 {
161 File licenseFile = new File( project.getBasedir(), url );
162 if ( !licenseFile.exists() )
163 {
164
165
166 licenseFile = new File( url );
167 }
168 if ( !licenseFile.exists() )
169 {
170 throw new IOException( "Maven can't find the file '" + licenseFile + "' on the system." );
171 }
172 try
173 {
174 licenseUrl = licenseFile.toURI().toURL();
175 }
176 catch ( MalformedURLException e )
177 {
178 throw new MalformedURLException( "The license url '" + url + "' seems to be invalid: "
179 + e.getMessage() );
180 }
181 }
182
183 return licenseUrl;
184 }
185
186
187
188
189
190
191
192
193 private static class LicenseRenderer
194 extends AbstractProjectInfoRenderer
195 {
196 private MavenProject project;
197
198 private Settings settings;
199
200 LicenseRenderer( Sink sink, MavenProject project, I18N i18n, Locale locale, Settings settings )
201 {
202 super( sink, i18n, locale );
203
204 this.project = project;
205
206 this.settings = settings;
207 }
208
209 protected String getI18Nsection()
210 {
211 return "license";
212 }
213
214
215 public void renderBody()
216 {
217 List licenses = project.getModel().getLicenses();
218
219 if ( licenses.isEmpty() )
220 {
221 startSection( getTitle() );
222
223 paragraph( getI18nString( "nolicense" ) );
224
225 endSection();
226
227 return;
228 }
229
230
231 startSection( getI18nString( "overview.title" ) );
232
233 paragraph( getI18nString( "overview.intro" ) );
234
235 endSection();
236
237
238 startSection( getI18nString( "title" ) );
239
240 for ( Iterator i = licenses.iterator(); i.hasNext(); )
241 {
242 License license = (License) i.next();
243
244 String name = license.getName();
245 String url = license.getUrl();
246 String comments = license.getComments();
247
248 startSection( name );
249
250 if ( !StringUtils.isEmpty( comments ) )
251 {
252 paragraph( comments );
253 }
254
255 if ( url != null )
256 {
257 URL licenseUrl = null;
258 try
259 {
260 licenseUrl = getLicenseURL( project, url );
261 }
262 catch ( MalformedURLException e )
263 {
264
265 paragraph( e.getMessage() );
266 }
267 catch ( IOException e )
268 {
269
270 paragraph( e.getMessage() );
271 }
272
273 if ( licenseUrl != null )
274 {
275 String licenseContent = null;
276 try
277 {
278
279 licenseContent = ProjectInfoReportUtils.getInputStream( licenseUrl, settings );
280 }
281 catch ( IOException e )
282 {
283 paragraph( "Can't read the url [" + licenseUrl + "] : " + e.getMessage() );
284 }
285
286 if ( licenseContent != null )
287 {
288
289 String licenseContentLC = licenseContent.toLowerCase( Locale.ENGLISH );
290 int bodyStart = licenseContentLC.indexOf( "<body" );
291 int bodyEnd = licenseContentLC.indexOf( "</body>" );
292 if ( ( licenseContentLC.startsWith( "<!doctype html" )
293 || licenseContentLC.startsWith( "<html>" ) ) && bodyStart >= 0 && bodyEnd >= 0 )
294 {
295 bodyStart = licenseContentLC.indexOf( ">", bodyStart ) + 1;
296 String body = licenseContent.substring( bodyStart, bodyEnd );
297
298 link( licenseUrl.toExternalForm(), "[Original text]" );
299 paragraph( "Copy of the license follows." );
300
301 body = replaceRelativeLinks( body, baseURL( licenseUrl ).toExternalForm() );
302 sink.rawText( body );
303 }
304 else
305 {
306 verbatimText( licenseContent );
307 }
308 }
309 }
310 }
311
312 endSection();
313 }
314
315 endSection();
316 }
317
318 private static URL baseURL( URL aUrl )
319 {
320 String urlTxt = aUrl.toExternalForm();
321 int lastSlash = urlTxt.lastIndexOf( '/' );
322 if ( lastSlash > -1 )
323 {
324 try
325 {
326 return new URL( urlTxt.substring( 0, lastSlash + 1 ) );
327 }
328 catch ( MalformedURLException e )
329 {
330 throw new AssertionError( e );
331 }
332 }
333
334 return aUrl;
335 }
336
337 private static String replaceRelativeLinks( String html, String baseURL )
338 {
339 String url = baseURL;
340 if ( !url.endsWith( "/" ) )
341 {
342 url += "/";
343 }
344
345 String serverURL = url.substring( 0, url.indexOf( '/', url.indexOf( "//" ) + 2 ) );
346
347 String content = replaceParts( html, url, serverURL, "[aA]", "[hH][rR][eE][fF]" );
348 content = replaceParts( content, url, serverURL, "[iI][mM][gG]", "[sS][rR][cC]" );
349 return content;
350 }
351
352 private static String replaceParts( String html, String baseURL, String serverURL, String tagPattern,
353 String attributePattern )
354 {
355 Pattern anchor = Pattern.compile(
356 "(<\\s*" + tagPattern + "\\s+[^>]*" + attributePattern + "\\s*=\\s*\")([^\"]*)\"([^>]*>)" );
357 StringBuffer sb = new StringBuffer( html );
358
359 int indx = 0;
360 boolean done = false;
361 while ( !done )
362 {
363 Matcher mAnchor = anchor.matcher( sb );
364 if ( mAnchor.find( indx ) )
365 {
366 indx = mAnchor.end( 3 );
367
368 if ( mAnchor.group( 2 ).startsWith( "#" ) )
369 {
370
371 }
372 if ( mAnchor.group( 2 ).startsWith( "/" ) )
373 {
374
375 sb.insert( mAnchor.start( 2 ), serverURL );
376 indx += serverURL.length();
377 }
378 else if ( mAnchor.group( 2 ).indexOf( ':' ) < 0 )
379 {
380
381 sb.insert( mAnchor.start( 2 ), baseURL );
382 indx += baseURL.length();
383 }
384 }
385 else
386 {
387 done = true;
388 }
389 }
390 return sb.toString();
391 }
392 }
393 }