1//
2//  ========================================================================
3//  Copyright (c) 1995-2014 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
20package org.eclipse.jetty.webapp;
21
22import java.util.ArrayList;
23import java.util.List;
24import java.util.StringTokenizer;
25
26
27/* ------------------------------------------------------------ */
28/**
29 * ClasspathPattern performs sequential pattern matching of a class name
30 * against an internal array of classpath pattern entries.
31 *
32 * When an entry starts with '-' (minus), reverse matching is performed.
33 * When an entry ends with '.' (period), prefix matching is performed.
34 *
35 * When class is initialized from a classpath pattern string, entries
36 * in this string should be separated by ':' (semicolon) or ',' (comma).
37 */
38
39public class ClasspathPattern
40{
41    private static class Entry
42    {
43        public String classpath = null;
44        public boolean result = false;
45        public boolean partial = false;
46    }
47
48    final private List<String> _patterns = new ArrayList<String>();
49    final private List<Entry> _entries = new ArrayList<Entry>();
50
51    /* ------------------------------------------------------------ */
52    public ClasspathPattern()
53    {
54    }
55
56    /* ------------------------------------------------------------ */
57    public ClasspathPattern(String[] patterns)
58    {
59        setPatterns(patterns);
60    }
61
62    /* ------------------------------------------------------------ */
63    public ClasspathPattern(String pattern)
64    {
65        setPattern(pattern);
66    }
67
68
69    /* ------------------------------------------------------------ */
70    /**
71     * Initialize the matcher by parsing each classpath pattern in an array
72     *
73     * @param patterns array of classpath patterns
74     */
75    private void setPatterns(String[] patterns)
76    {
77        _patterns.clear();
78        _entries.clear();
79        addPatterns(patterns);
80    }
81
82    /* ------------------------------------------------------------ */
83    /**
84     * Initialize the matcher by parsing each classpath pattern in an array
85     *
86     * @param patterns array of classpath patterns
87     */
88    private void addPatterns(String[] patterns)
89    {
90        if (patterns != null)
91        {
92            Entry entry = null;
93            for (String pattern : patterns)
94            {
95                entry = createEntry(pattern);
96                if (entry != null) {
97                    _patterns.add(pattern);
98                    _entries.add(entry);
99                }
100            }
101        }
102    }
103
104    /* ------------------------------------------------------------ */
105    /**
106     * Create an entry object containing information about
107     * a single classpath pattern
108     *
109     * @param pattern single classpath pattern
110     * @return corresponding Entry object
111     */
112    private Entry createEntry(String pattern)
113    {
114        Entry entry = null;
115
116        if (pattern != null)
117        {
118            String item = pattern.trim();
119            if (item.length() > 0)
120            {
121                entry = new Entry();
122                entry.result = !item.startsWith("-");
123                entry.partial = item.endsWith(".");
124                entry.classpath = entry.result ? item : item.substring(1).trim();
125            }
126        }
127        return entry;
128    }
129
130    /* ------------------------------------------------------------ */
131    /**
132     * Initialize the matcher by parsing a classpath pattern string
133     *
134     * @param pattern classpath pattern string
135     */
136    public void setPattern(String pattern)
137    {
138        _patterns.clear();
139        _entries.clear();
140        addPattern(pattern);
141    }
142
143    /* ------------------------------------------------------------ */
144    /**
145     * Parse a classpath pattern string and appending the result
146     * to the existing configuration.
147     *
148     * @param pattern classpath pattern string
149     */
150    public void addPattern(String pattern)
151    {
152        ArrayList<String> patterns = new ArrayList<String>();
153        StringTokenizer entries = new StringTokenizer(pattern, ":,");
154        while (entries.hasMoreTokens())
155        {
156            patterns.add(entries.nextToken());
157        }
158
159        addPatterns((String[])patterns.toArray(new String[patterns.size()]));
160    }
161
162    /* ------------------------------------------------------------ */
163    /**
164     * @return array of classpath patterns
165     */
166    public String[] getPatterns()
167    {
168        String[] patterns = null;
169
170        if (_patterns!=null && _patterns.size() > 0)
171        {
172            patterns = _patterns.toArray(new String[_patterns.size()]);
173        }
174
175        return patterns;
176    }
177
178    /* ------------------------------------------------------------ */
179    /**
180     * Match the class name against the pattern
181     *
182     * @param name name of the class to match
183     * @return true if class matches the pattern
184     */
185    public boolean match(String name)
186    {
187        boolean result=false;
188
189        if (_entries != null)
190        {
191            name = name.replace('/','.');
192
193            int startIndex = 0;
194
195            while(startIndex < name.length() && name.charAt(startIndex) == '.') {
196                startIndex++;
197            }
198
199            int dollar = name.indexOf("$");
200
201            int endIndex =  dollar != -1 ? dollar : name.length();
202
203            for (Entry entry : _entries)
204            {
205                if (entry != null)
206                {
207                    if (entry.partial)
208                    {
209                        if (name.regionMatches(startIndex, entry.classpath, 0, entry.classpath.length()))
210                        {
211                            result = entry.result;
212                            break;
213                        }
214                    }
215                    else
216                    {
217                        int regionLength = endIndex-startIndex;
218                        if (regionLength == entry.classpath.length()
219                                && name.regionMatches(startIndex, entry.classpath, 0, regionLength))
220                        {
221                            result = entry.result;
222                            break;
223                        }
224                    }
225                }
226            }
227        }
228        return result;
229    }
230}
231