View Javadoc
1   package org.apache.maven.plugins.shade.relocation;
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.codehaus.plexus.util.SelectorUtils;
23  
24  import java.util.Collection;
25  import java.util.LinkedHashSet;
26  import java.util.List;
27  import java.util.Set;
28  import java.util.regex.Pattern;
29  
30  /**
31   * @author Jason van Zyl
32   * @author Mauro Talevi
33   */
34  public class SimpleRelocator
35      implements Relocator
36  {
37  
38      private final String pattern;
39  
40      private final String pathPattern;
41  
42      private final String shadedPattern;
43  
44      private final String shadedPathPattern;
45  
46      private final Set<String> includes;
47  
48      private final Set<String> excludes;
49  
50      private final boolean rawString;
51  
52      public SimpleRelocator( String patt, String shadedPattern, List<String> includes, List<String> excludes )
53      {
54          this( patt, shadedPattern, includes, excludes, false );
55      }
56  
57      public SimpleRelocator( String patt, String shadedPattern, List<String> includes, List<String> excludes,
58                              boolean rawString )
59      {
60          this.rawString = rawString;
61  
62          if ( rawString )
63          {
64              this.pathPattern = patt;
65              this.shadedPathPattern = shadedPattern;
66  
67              this.pattern = null; // not used for raw string relocator
68              this.shadedPattern = null; // not used for raw string relocator
69          }
70          else
71          {
72              if ( patt == null )
73              {
74                  this.pattern = "";
75                  this.pathPattern = "";
76              }
77              else
78              {
79                  this.pattern = patt.replace( '/', '.' );
80                  this.pathPattern = patt.replace( '.', '/' );
81              }
82  
83              if ( shadedPattern != null )
84              {
85                  this.shadedPattern = shadedPattern.replace( '/', '.' );
86                  this.shadedPathPattern = shadedPattern.replace( '.', '/' );
87              }
88              else
89              {
90                  this.shadedPattern = "hidden." + this.pattern;
91                  this.shadedPathPattern = "hidden/" + this.pathPattern;
92              }
93          }
94  
95          this.includes = normalizePatterns( includes );
96          this.excludes = normalizePatterns( excludes );
97  
98          // Don't replace all dots to slashes, otherwise /META-INF/maven/${groupId} can't be matched.
99          if ( includes != null && !includes.isEmpty() )
100         {
101             this.includes.addAll( includes );
102         }
103         
104         if ( excludes != null && !excludes.isEmpty() )
105         {
106             this.excludes.addAll( excludes );
107         }
108     }
109 
110     private static Set<String> normalizePatterns( Collection<String> patterns )
111     {
112         Set<String> normalized = null;
113 
114         if ( patterns != null && !patterns.isEmpty() )
115         {
116             normalized = new LinkedHashSet<String>();
117 
118             for ( String pattern : patterns )
119             {
120 
121                 String classPattern = pattern.replace( '.', '/' );
122 
123                 normalized.add( classPattern );
124 
125                 if ( classPattern.endsWith( "/*" ) )
126                 {
127                     String packagePattern = classPattern.substring( 0, classPattern.lastIndexOf( '/' ) );
128                     normalized.add( packagePattern );
129                 }
130             }
131         }
132 
133         return normalized;
134     }
135 
136     private boolean isIncluded( String path )
137     {
138         if ( includes != null && !includes.isEmpty() )
139         {
140             for ( String include : includes )
141             {
142                 if ( SelectorUtils.matchPath( include, path, true ) )
143                 {
144                     return true;
145                 }
146             }
147             return false;
148         }
149         return true;
150     }
151 
152     private boolean isExcluded( String path )
153     {
154         if ( excludes != null && !excludes.isEmpty() )
155         {
156             for ( String exclude : excludes )
157             {
158                 if ( SelectorUtils.matchPath( exclude, path, true ) )
159                 {
160                     return true;
161                 }
162             }
163         }
164         return false;
165     }
166 
167     public boolean canRelocatePath( String path )
168     {
169         if ( rawString )
170         {
171             return Pattern.compile( pathPattern ).matcher( path ).find();
172         }
173 
174         if ( path.endsWith( ".class" ) )
175         {
176             path = path.substring( 0, path.length() - 6 );
177         }
178 
179         // Allow for annoying option of an extra / on the front of a path. See MSHADE-119; comes from
180         // getClass().getResource("/a/b/c.properties").
181         if ( !path.isEmpty() && path.charAt( 0 ) == '/' )
182         {
183             path = path.substring( 1 );
184         }
185 
186         return isIncluded( path ) && !isExcluded( path ) && path.startsWith( pathPattern );
187     }
188 
189     public boolean canRelocateClass( String clazz )
190     {
191         return !rawString && clazz.indexOf( '/' ) < 0 && canRelocatePath( clazz.replace( '.', '/' ) );
192     }
193 
194     public String relocatePath( String path )
195     {
196         if ( rawString )
197         {
198             return path.replaceAll( pathPattern, shadedPathPattern );
199         }
200         else
201         {
202             return path.replaceFirst( pathPattern, shadedPathPattern );
203         }
204     }
205 
206     public String relocateClass( String clazz )
207     {
208         return clazz.replaceFirst( pattern, shadedPattern );
209     }
210 
211     public String applyToSourceContent( String sourceContent )
212     {
213         if ( rawString )
214         {
215             return sourceContent;
216         }
217         else
218         {
219             return sourceContent.replaceAll( "\\b" + pattern, shadedPattern );
220         }
221     }
222 }