View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  
20  package org.eclipse.jetty.webapp;
21  
22  import java.util.AbstractList;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.List;
26  import java.util.ListIterator;
27  
28  import org.eclipse.jetty.util.StringUtil;
29  
30  /* ------------------------------------------------------------ */
31  /**
32   * Classpath classes list performs sequential pattern matching of a class name 
33   * against an internal array of classpath pattern entries.
34   * A class pattern is a string of one of the forms:<ul>
35   * <li>'org.package.SomeClass' will match a specific class
36   * <li>'org.package.' will match a specific package hierarchy
37   * <li>'-org.package.Classname' excludes a specific class
38   * <li>'-org.package.' excludes a specific package hierarchy
39   * <li>Nested classes must be specified with the '$' separator if they 
40   * are to be explicitly included or excluded (eg. org.example.MyClass$NestedClass).
41   * <li>Nested classes are matched by their containing class. (eg. -org.example.MyClass
42   * would exclude org.example.MyClass$AnyNestedClass)
43   * </ul>
44   * When class is initialized from a classpath pattern string, entries 
45   * in this string should be separated by ':' (semicolon) or ',' (comma).
46   */
47  
48  public class ClasspathPattern extends AbstractList<String>
49  {
50      private static class Entry
51      {
52          public final String _pattern;
53          public final String _name;
54          public final boolean _inclusive;
55          public final boolean _package;     
56          
57          Entry(String pattern)
58          {
59              _pattern=pattern;
60              _inclusive = !pattern.startsWith("-");
61              _package = pattern.endsWith(".");
62              _name = _inclusive ? pattern : pattern.substring(1).trim();
63          }
64          
65          @Override
66          public String toString()
67          {
68              return _pattern;
69          }
70      }
71      
72      final private List<Entry> _entries = new ArrayList<Entry>();
73      
74      /* ------------------------------------------------------------ */
75      public ClasspathPattern()
76      {
77      }
78      
79      /* ------------------------------------------------------------ */
80      public ClasspathPattern(String[] patterns)
81      {
82          setAll(patterns);
83      }
84      
85      /* ------------------------------------------------------------ */
86      public ClasspathPattern(String pattern)
87      {
88          add(pattern);
89      }
90      
91      /* ------------------------------------------------------------ */
92      @Override
93      public String get(int index)
94      {
95          return _entries.get(index)._pattern;
96      }
97  
98      /* ------------------------------------------------------------ */
99      @Override
100     public String set(int index, String element)
101     {
102         Entry e = _entries.set(index,new Entry(element));
103         return e==null?null:e._pattern;
104     }
105 
106     /* ------------------------------------------------------------ */
107     @Override
108     public void add(int index, String element)
109     {
110         _entries.add(index,new Entry(element));
111     }
112 
113     /* ------------------------------------------------------------ */
114     @Deprecated
115     public void addPattern(String element)
116     {
117         add(element);
118     }
119     
120     /* ------------------------------------------------------------ */
121     @Override
122     public String remove(int index)
123     {
124         Entry e = _entries.remove(index);
125         return e==null?null:e._pattern;
126     }
127     
128     /* ------------------------------------------------------------ */
129     public boolean remove(String pattern)
130     {
131         for (int i=_entries.size();i-->0;)
132         {
133             if (pattern.equals(_entries.get(i)._pattern))
134             {
135                 _entries.remove(i);
136                 return true;
137             }
138         }
139         return false;
140     }
141 
142     /* ------------------------------------------------------------ */
143     @Override
144     public int size()
145     {
146         return _entries.size();
147     }
148 
149     /* ------------------------------------------------------------ */
150     /**
151      * Initialize the matcher by parsing each classpath pattern in an array
152      * 
153      * @param classes array of classpath patterns
154      */
155     private void setAll(String[] classes)
156     {
157         _entries.clear();
158         addAll(classes);
159     }
160     
161     /* ------------------------------------------------------------ */
162     /**
163      * @param classes array of classpath patterns
164      */
165     private void addAll(String[] classes)
166     {
167         if (classes!=null)
168             addAll(Arrays.asList(classes));
169     }
170     
171     /* ------------------------------------------------------------ */
172     /**
173      * @param classes array of classpath patterns
174      */
175     public void prepend(String[] classes)
176     {
177         if (classes != null)
178         {
179             int i=0;
180             for (String c : classes)
181             {
182                 add(i,c);
183                 i++;
184             }
185         }
186     }
187 
188     /* ------------------------------------------------------------ */
189     public void prependPattern(String pattern)
190     {
191         add(0,pattern);
192     }
193     
194     /* ------------------------------------------------------------ */
195     /**
196      * @return array of classpath patterns
197      */
198     public String[] getPatterns()
199     {
200         return toArray(new String[_entries.size()]);
201     }
202 
203     /* ------------------------------------------------------------ */
204     /**
205      * @return List of classes excluded class exclusions and package patterns
206      */
207     public List<String> getClasses()
208     {
209         List<String> list = new ArrayList<>();
210         for (Entry e:_entries)
211         {
212             if (e._inclusive && !e._package)
213                 list.add(e._name);
214         }
215         return list;
216     }
217     
218     /* ------------------------------------------------------------ */
219     /**
220      * Match the class name against the pattern
221      *
222      * @param name name of the class to match
223      * @return true if class matches the pattern
224      */
225     public boolean match(String name)
226     {       
227         name = name.replace('/','.');
228 
229         for (Entry entry : _entries)
230         {
231             if (entry==null)
232                 continue;
233             if (entry._package)
234             {
235                 if (name.startsWith(entry._name))
236                     return entry._inclusive;
237             }
238             else
239             {
240                 if (name.equals(entry._name))
241                     return entry._inclusive;
242                 
243                 if (name.length()>entry._name.length() && '$'==name.charAt(entry._name.length()) && name.startsWith(entry._name))
244                     return entry._inclusive;
245             }
246         }
247         return false;
248     }
249 
250     public void addAfter(String afterPattern,String... patterns)
251     {
252         if (patterns!=null && afterPattern!=null)
253         {
254             ListIterator<String> iter = listIterator();
255             while (iter.hasNext())
256             {
257                 String cc=iter.next();
258                 if (afterPattern.equals(cc))
259                 {
260                     for (int i=0;i<patterns.length;i++)
261                         iter.add(patterns[i]);
262                     return;
263                 }
264             }
265         }
266         throw new IllegalArgumentException("after '"+afterPattern+"' not found in "+this);
267     }
268 
269     public void addBefore(String beforePattern,String... patterns)
270     {
271         if (patterns!=null && beforePattern!=null)
272         {
273             ListIterator<String> iter = listIterator();
274             while (iter.hasNext())
275             {
276                 String cc=iter.next();
277                 if (beforePattern.equals(cc))
278                 {
279                     iter.previous();
280                     for (int i=0;i<patterns.length;i++)
281                         iter.add(patterns[i]);
282                     return;
283                 }
284             }
285         }
286         throw new IllegalArgumentException("before '"+beforePattern+"' not found in "+this);
287     }
288 }