1 package org.apache.maven.plugins.shade.resource;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.plugins.shade.relocation.Relocator;
23 import org.codehaus.plexus.util.StringUtils;
24
25 import java.io.BufferedReader;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.InputStreamReader;
29 import java.io.OutputStreamWriter;
30 import java.io.Writer;
31 import java.text.SimpleDateFormat;
32 import java.util.Date;
33 import java.util.LinkedHashMap;
34 import java.util.LinkedHashSet;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Set;
38 import java.util.TreeSet;
39 import java.util.jar.JarEntry;
40 import java.util.jar.JarOutputStream;
41
42
43
44
45 public class ApacheNoticeResourceTransformer
46 extends AbstractCompatibilityTransformer
47 {
48 Set<String> entries = new LinkedHashSet<>();
49
50 Map<String, Set<String>> organizationEntries = new LinkedHashMap<>();
51
52 String projectName = "";
53
54 boolean addHeader = true;
55
56 String preamble1 = "// ------------------------------------------------------------------\n"
57 + "// NOTICE file corresponding to the section 4d of The Apache License,\n"
58 + "// Version 2.0, in this case for ";
59
60 String preamble2 = "\n// ------------------------------------------------------------------\n";
61
62 String preamble3 = "This product includes software developed at\n";
63
64
65 String organizationName = "The Apache Software Foundation";
66
67 String organizationURL = "http://www.apache.org/";
68
69 String inceptionYear = "2006";
70
71 String copyright;
72
73
74
75
76 String encoding;
77
78 private long time = Long.MIN_VALUE;
79
80 private static final String NOTICE_PATH = "META-INF/NOTICE";
81
82 private static final String NOTICE_TXT_PATH = "META-INF/NOTICE.txt";
83
84 private static final String NOTICE_MD_PATH = "META-INF/NOTICE.md";
85
86 public boolean canTransformResource( String resource )
87 {
88 return NOTICE_PATH.equalsIgnoreCase( resource )
89 || NOTICE_TXT_PATH.equalsIgnoreCase( resource )
90 || NOTICE_MD_PATH.equalsIgnoreCase( resource );
91
92 }
93
94 public void processResource( String resource, InputStream is, List<Relocator> relocators, long time )
95 throws IOException
96 {
97 if ( entries.isEmpty() )
98 {
99 String year = new SimpleDateFormat( "yyyy" ).format( new Date() );
100 if ( !inceptionYear.equals( year ) )
101 {
102 year = inceptionYear + "-" + year;
103 }
104
105
106 if ( addHeader )
107 {
108 entries.add( preamble1 + projectName + preamble2 );
109 }
110 else
111 {
112 entries.add( "" );
113 }
114
115 entries.add( projectName + "\nCopyright " + year + " " + organizationName + "\n" );
116 entries.add( preamble3 + organizationName + " (" + organizationURL + ").\n" );
117 }
118
119 BufferedReader reader;
120 if ( StringUtils.isNotEmpty( encoding ) )
121 {
122 reader = new BufferedReader( new InputStreamReader( is, encoding ) );
123 }
124 else
125 {
126 reader = new BufferedReader( new InputStreamReader( is ) );
127 }
128
129 String line = reader.readLine();
130 StringBuilder sb = new StringBuilder();
131 Set<String> currentOrg = null;
132 int lineCount = 0;
133 while ( line != null )
134 {
135 String trimedLine = line.trim();
136
137 if ( !trimedLine.startsWith( "//" ) )
138 {
139 if ( trimedLine.length() > 0 )
140 {
141 if ( trimedLine.startsWith( "- " ) )
142 {
143
144 if ( lineCount == 1
145 && sb.toString().contains( "This product includes/uses software(s) developed by" ) )
146 {
147 currentOrg = organizationEntries.get( sb.toString().trim() );
148 if ( currentOrg == null )
149 {
150 currentOrg = new TreeSet<>();
151 organizationEntries.put( sb.toString().trim(), currentOrg );
152 }
153 sb = new StringBuilder();
154 }
155 else if ( sb.length() > 0 && currentOrg != null )
156 {
157 currentOrg.add( sb.toString() );
158 sb = new StringBuilder();
159 }
160
161 }
162 sb.append( line ).append( "\n" );
163 lineCount++;
164 }
165 else
166 {
167 String ent = sb.toString();
168 if ( ent.startsWith( projectName ) && ent.contains( "Copyright " ) )
169 {
170 copyright = ent;
171 }
172 if ( currentOrg == null )
173 {
174 entries.add( ent );
175 }
176 else
177 {
178 currentOrg.add( ent );
179 }
180 sb = new StringBuilder();
181 lineCount = 0;
182 currentOrg = null;
183 }
184 }
185
186 line = reader.readLine();
187 }
188 if ( sb.length() > 0 )
189 {
190 if ( currentOrg == null )
191 {
192 entries.add( sb.toString() );
193 }
194 else
195 {
196 currentOrg.add( sb.toString() );
197 }
198 }
199 if ( time > this.time )
200 {
201 this.time = time;
202 }
203 }
204
205 public boolean hasTransformedResource()
206 {
207 return true;
208 }
209
210 public void modifyOutputStream( JarOutputStream jos )
211 throws IOException
212 {
213 JarEntry jarEntry = new JarEntry( NOTICE_PATH );
214 jarEntry.setTime( time );
215 jos.putNextEntry( jarEntry );
216
217 Writer writer;
218 if ( StringUtils.isNotEmpty( encoding ) )
219 {
220 writer = new OutputStreamWriter( jos, encoding );
221 }
222 else
223 {
224 writer = new OutputStreamWriter( jos );
225 }
226
227 int count = 0;
228 for ( String line : entries )
229 {
230 ++count;
231 if ( line.equals( copyright ) && count != 2 )
232 {
233 continue;
234 }
235
236 if ( count == 2 && copyright != null )
237 {
238 writer.write( copyright );
239 writer.write( '\n' );
240 }
241 else
242 {
243 writer.write( line );
244 writer.write( '\n' );
245 }
246 if ( count == 3 )
247 {
248
249 for ( Map.Entry<String, Set<String>> entry : organizationEntries.entrySet() )
250 {
251 writer.write( entry.getKey() );
252 writer.write( '\n' );
253 for ( String l : entry.getValue() )
254 {
255 writer.write( l );
256 }
257 writer.write( '\n' );
258 }
259 }
260 }
261
262 writer.flush();
263
264 entries.clear();
265 }
266 }