View Javadoc
1   package org.apache.maven.plugins.shade.resource;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
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.PrintWriter;
31  import java.io.Writer;
32  import java.text.SimpleDateFormat;
33  import java.util.Date;
34  import java.util.LinkedHashMap;
35  import java.util.LinkedHashSet;
36  import java.util.List;
37  import java.util.Map;
38  import java.util.Set;
39  import java.util.TreeSet;
40  import java.util.jar.JarEntry;
41  import java.util.jar.JarOutputStream;
42  
43  /**
44   * Merges <code>META-INF/NOTICE.TXT</code> files.
45   */
46  public class ApacheNoticeResourceTransformer
47      implements ResourceTransformer
48  {
49      Set<String> entries = new LinkedHashSet<String>();
50  
51      Map<String, Set<String>> organizationEntries = new LinkedHashMap<String, Set<String>>();
52  
53      String projectName = ""; // MSHADE-101 :: NullPointerException when projectName is missing
54  
55      boolean addHeader = true;
56  
57      String preamble1 = "// ------------------------------------------------------------------\n"
58          + "// NOTICE file corresponding to the section 4d of The Apache License,\n"
59          + "// Version 2.0, in this case for ";
60  
61      String preamble2 = "\n// ------------------------------------------------------------------\n";
62  
63      String preamble3 = "This product includes software developed at\n";
64  
65      //defaults overridable via config in pom
66      String organizationName = "The Apache Software Foundation";
67  
68      String organizationURL = "http://www.apache.org/";
69  
70      String inceptionYear = "2006";
71  
72      String copyright;
73  
74      /**
75       * The file encoding of the <code>NOTICE</code> file.
76       */
77      String encoding;
78  
79      private static final String NOTICE_PATH = "META-INF/NOTICE";
80  
81      private static final String NOTICE_TXT_PATH = "META-INF/NOTICE.txt";
82  
83      public boolean canTransformResource( String resource )
84      {
85          if ( NOTICE_PATH.equalsIgnoreCase( resource ) || NOTICE_TXT_PATH.equalsIgnoreCase( resource ) )
86          {
87              return true;
88          }
89  
90          return false;
91      }
92  
93      public void processResource( String resource, InputStream is, List<Relocator> relocators )
94          throws IOException
95      {
96          if ( entries.isEmpty() )
97          {
98              String year = new SimpleDateFormat( "yyyy" ).format( new Date() );
99              if ( !inceptionYear.equals( year ) )
100             {
101                 year = inceptionYear + "-" + year;
102             }
103 
104             //add headers
105             if ( addHeader )
106             {
107                 entries.add( preamble1 + projectName + preamble2 );
108             }
109             else
110             {
111                 entries.add( "" );
112             }
113             //fake second entry, we'll look for a real one later
114             entries.add( projectName + "\nCopyright " + year + " " + organizationName + "\n" );
115             entries.add( preamble3 + organizationName + " (" + organizationURL + ").\n" );
116         }
117 
118         BufferedReader reader;
119         if ( StringUtils.isNotEmpty( encoding ) )
120         {
121             reader = new BufferedReader( new InputStreamReader( is, encoding ) );
122         }
123         else
124         {
125             reader = new BufferedReader( new InputStreamReader( is ) );
126         }
127 
128         String line = reader.readLine();
129         StringBuilder sb = new StringBuilder();
130         Set<String> currentOrg = null;
131         int lineCount = 0;
132         while ( line != null )
133         {
134             String trimedLine = line.trim();
135 
136             if ( !trimedLine.startsWith( "//" ) )
137             {
138                 if ( trimedLine.length() > 0 )
139                 {
140                     if ( trimedLine.startsWith( "- " ) )
141                     {
142                         //resource-bundle 1.3 mode
143                         if ( lineCount == 1
144                             && sb.toString().contains("This product includes/uses software(s) developed by"))
145                         {
146                             currentOrg = organizationEntries.get( sb.toString().trim() );
147                             if ( currentOrg == null )
148                             {
149                                 currentOrg = new TreeSet<String>();
150                                 organizationEntries.put( sb.toString().trim(), currentOrg );
151                             }
152                             sb = new StringBuilder();
153                         }
154                         else if ( sb.length() > 0 && currentOrg != null )
155                         {
156                             currentOrg.add( sb.toString() );
157                             sb = new StringBuilder();
158                         }
159 
160                     }
161                     sb.append( line ).append( "\n" );
162                     lineCount++;
163                 }
164                 else
165                 {
166                     String ent = sb.toString();
167                     if ( ent.startsWith( projectName ) && ent.contains("Copyright "))
168                     {
169                         copyright = ent;
170                     }
171                     if ( currentOrg == null )
172                     {
173                         entries.add( ent );
174                     }
175                     else
176                     {
177                         currentOrg.add( ent );
178                     }
179                     sb = new StringBuilder();
180                     lineCount = 0;
181                     currentOrg = null;
182                 }
183             }
184 
185             line = reader.readLine();
186         }
187         if ( sb.length() > 0 )
188         {
189             if ( currentOrg == null )
190             {
191                 entries.add( sb.toString() );
192             }
193             else
194             {
195                 currentOrg.add( sb.toString() );
196             }
197         }
198     }
199 
200     public boolean hasTransformedResource()
201     {
202         return true;
203     }
204 
205     public void modifyOutputStream( JarOutputStream jos )
206         throws IOException
207     {
208         jos.putNextEntry( new JarEntry( NOTICE_PATH ) );
209 
210         Writer pow;
211         if ( StringUtils.isNotEmpty( encoding ) )
212         {
213             pow = new OutputStreamWriter( jos, encoding );
214         }
215         else
216         {
217             pow = new OutputStreamWriter( jos );
218         }
219         PrintWriter writer = new PrintWriter( pow );
220 
221         int count = 0;
222         for ( String line : entries )
223         {
224             ++count;
225             if ( line.equals( copyright ) && count != 2 )
226             {
227                 continue;
228             }
229 
230             if ( count == 2 && copyright != null )
231             {
232                 writer.print( copyright );
233                 writer.print( '\n' );
234             }
235             else
236             {
237                 writer.print( line );
238                 writer.print( '\n' );
239             }
240             if ( count == 3 )
241             {
242                 //do org stuff
243                 for ( Map.Entry<String, Set<String>> entry : organizationEntries.entrySet() )
244                 {
245                     writer.print( entry.getKey() );
246                     writer.print( '\n' );
247                     for ( String l : entry.getValue() )
248                     {
249                         writer.print( l );
250                     }
251                     writer.print( '\n' );
252                 }
253             }
254         }
255 
256         writer.flush();
257 
258         entries.clear();
259     }
260 }