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.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   * Merges <code>META-INF/NOTICE.TXT</code> files.
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 = ""; // MSHADE-101 :: NullPointerException when projectName is missing
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      //defaults overridable via config in pom
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       * The file encoding of the <code>NOTICE</code> file.
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      public boolean canTransformResource( String resource )
85      {
86          return NOTICE_PATH.equalsIgnoreCase( resource ) || NOTICE_TXT_PATH.equalsIgnoreCase( resource );
87  
88      }
89  
90      public void processResource( String resource, InputStream is, List<Relocator> relocators, long time )
91          throws IOException
92      {
93          if ( entries.isEmpty() )
94          {
95              String year = new SimpleDateFormat( "yyyy" ).format( new Date() );
96              if ( !inceptionYear.equals( year ) )
97              {
98                  year = inceptionYear + "-" + year;
99              }
100 
101             //add headers
102             if ( addHeader )
103             {
104                 entries.add( preamble1 + projectName + preamble2 );
105             }
106             else
107             {
108                 entries.add( "" );
109             }
110             //fake second entry, we'll look for a real one later
111             entries.add( projectName + "\nCopyright " + year + " " + organizationName + "\n" );
112             entries.add( preamble3 + organizationName + " (" + organizationURL + ").\n" );
113         }
114 
115         BufferedReader reader;
116         if ( StringUtils.isNotEmpty( encoding ) )
117         {
118             reader = new BufferedReader( new InputStreamReader( is, encoding ) );
119         }
120         else
121         {
122             reader = new BufferedReader( new InputStreamReader( is ) );
123         }
124 
125         String line = reader.readLine();
126         StringBuilder sb = new StringBuilder();
127         Set<String> currentOrg = null;
128         int lineCount = 0;
129         while ( line != null )
130         {
131             String trimedLine = line.trim();
132 
133             if ( !trimedLine.startsWith( "//" ) )
134             {
135                 if ( trimedLine.length() > 0 )
136                 {
137                     if ( trimedLine.startsWith( "- " ) )
138                     {
139                         //resource-bundle 1.3 mode
140                         if ( lineCount == 1
141                             && sb.toString().contains( "This product includes/uses software(s) developed by" ) )
142                         {
143                             currentOrg = organizationEntries.get( sb.toString().trim() );
144                             if ( currentOrg == null )
145                             {
146                                 currentOrg = new TreeSet<>();
147                                 organizationEntries.put( sb.toString().trim(), currentOrg );
148                             }
149                             sb = new StringBuilder();
150                         }
151                         else if ( sb.length() > 0 && currentOrg != null )
152                         {
153                             currentOrg.add( sb.toString() );
154                             sb = new StringBuilder();
155                         }
156 
157                     }
158                     sb.append( line ).append( "\n" );
159                     lineCount++;
160                 }
161                 else
162                 {
163                     String ent = sb.toString();
164                     if ( ent.startsWith( projectName ) && ent.contains( "Copyright " ) )
165                     {
166                         copyright = ent;
167                     }
168                     if ( currentOrg == null )
169                     {
170                         entries.add( ent );
171                     }
172                     else
173                     {
174                         currentOrg.add( ent );
175                     }
176                     sb = new StringBuilder();
177                     lineCount = 0;
178                     currentOrg = null;
179                 }
180             }
181 
182             line = reader.readLine();
183         }
184         if ( sb.length() > 0 )
185         {
186             if ( currentOrg == null )
187             {
188                 entries.add( sb.toString() );
189             }
190             else
191             {
192                 currentOrg.add( sb.toString() );
193             }
194         }
195         if ( time > this.time )
196         {
197             this.time = time;        
198         }
199     }
200 
201     public boolean hasTransformedResource()
202     {
203         return true;
204     }
205 
206     public void modifyOutputStream( JarOutputStream jos )
207         throws IOException
208     {
209         JarEntry jarEntry = new JarEntry( NOTICE_PATH );
210         jarEntry.setTime( time );
211         jos.putNextEntry( jarEntry );
212 
213         Writer writer;
214         if ( StringUtils.isNotEmpty( encoding ) )
215         {
216             writer = new OutputStreamWriter( jos, encoding );
217         }
218         else
219         {
220             writer = new OutputStreamWriter( jos );
221         }
222 
223         int count = 0;
224         for ( String line : entries )
225         {
226             ++count;
227             if ( line.equals( copyright ) && count != 2 )
228             {
229                 continue;
230             }
231 
232             if ( count == 2 && copyright != null )
233             {
234                 writer.write( copyright );
235                 writer.write( '\n' );
236             }
237             else
238             {
239                 writer.write( line );
240                 writer.write( '\n' );
241             }
242             if ( count == 3 )
243             {
244                 //do org stuff
245                 for ( Map.Entry<String, Set<String>> entry : organizationEntries.entrySet() )
246                 {
247                     writer.write( entry.getKey() );
248                     writer.write( '\n' );
249                     for ( String l : entry.getValue() )
250                     {
251                         writer.write( l );
252                     }
253                     writer.write( '\n' );
254                 }
255             }
256         }
257 
258         writer.flush();
259 
260         entries.clear();
261     }
262 }