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