blob: de9fad80500c1a595bdbe96bbbb7abb2d464d07b [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.webapp;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;
import org.eclipse.jetty.util.StringUtil;
/* ------------------------------------------------------------ */
/**
* Classpath classes list performs sequential pattern matching of a class name
* against an internal array of classpath pattern entries.
* A class pattern is a string of one of the forms:<ul>
* <li>'org.package.SomeClass' will match a specific class
* <li>'org.package.' will match a specific package hierarchy
* <li>'-org.package.Classname' excludes a specific class
* <li>'-org.package.' excludes a specific package hierarchy
* <li>Nested classes must be specified with the '$' separator if they
* are to be explicitly included or excluded (eg. org.example.MyClass$NestedClass).
* <li>Nested classes are matched by their containing class. (eg. -org.example.MyClass
* would exclude org.example.MyClass$AnyNestedClass)
* </ul>
* When class is initialized from a classpath pattern string, entries
* in this string should be separated by ':' (semicolon) or ',' (comma).
*/
public class ClasspathPattern extends AbstractList<String>
{
private static class Entry
{
public final String _pattern;
public final String _name;
public final boolean _inclusive;
public final boolean _package;
Entry(String pattern)
{
_pattern=pattern;
_inclusive = !pattern.startsWith("-");
_package = pattern.endsWith(".");
_name = _inclusive ? pattern : pattern.substring(1).trim();
}
@Override
public String toString()
{
return _pattern;
}
}
final private List<Entry> _entries = new ArrayList<Entry>();
/* ------------------------------------------------------------ */
public ClasspathPattern()
{
}
/* ------------------------------------------------------------ */
public ClasspathPattern(String[] patterns)
{
setAll(patterns);
}
/* ------------------------------------------------------------ */
public ClasspathPattern(String pattern)
{
add(pattern);
}
/* ------------------------------------------------------------ */
@Override
public String get(int index)
{
return _entries.get(index)._pattern;
}
/* ------------------------------------------------------------ */
@Override
public String set(int index, String element)
{
Entry e = _entries.set(index,new Entry(element));
return e==null?null:e._pattern;
}
/* ------------------------------------------------------------ */
@Override
public void add(int index, String element)
{
_entries.add(index,new Entry(element));
}
/* ------------------------------------------------------------ */
@Deprecated
public void addPattern(String element)
{
add(element);
}
/* ------------------------------------------------------------ */
@Override
public String remove(int index)
{
Entry e = _entries.remove(index);
return e==null?null:e._pattern;
}
/* ------------------------------------------------------------ */
public boolean remove(String pattern)
{
for (int i=_entries.size();i-->0;)
{
if (pattern.equals(_entries.get(i)._pattern))
{
_entries.remove(i);
return true;
}
}
return false;
}
/* ------------------------------------------------------------ */
@Override
public int size()
{
return _entries.size();
}
/* ------------------------------------------------------------ */
/**
* Initialize the matcher by parsing each classpath pattern in an array
*
* @param classes array of classpath patterns
*/
private void setAll(String[] classes)
{
_entries.clear();
addAll(classes);
}
/* ------------------------------------------------------------ */
/**
* @param classes array of classpath patterns
*/
private void addAll(String[] classes)
{
if (classes!=null)
addAll(Arrays.asList(classes));
}
/* ------------------------------------------------------------ */
/**
* @param classes array of classpath patterns
*/
public void prepend(String[] classes)
{
if (classes != null)
{
int i=0;
for (String c : classes)
{
add(i,c);
i++;
}
}
}
/* ------------------------------------------------------------ */
public void prependPattern(String pattern)
{
add(0,pattern);
}
/* ------------------------------------------------------------ */
/**
* @return array of classpath patterns
*/
public String[] getPatterns()
{
return toArray(new String[_entries.size()]);
}
/* ------------------------------------------------------------ */
/**
* @return List of classes excluded class exclusions and package patterns
*/
public List<String> getClasses()
{
List<String> list = new ArrayList<>();
for (Entry e:_entries)
{
if (e._inclusive && !e._package)
list.add(e._name);
}
return list;
}
/* ------------------------------------------------------------ */
/**
* Match the class name against the pattern
*
* @param name name of the class to match
* @return true if class matches the pattern
*/
public boolean match(String name)
{
name = name.replace('/','.');
for (Entry entry : _entries)
{
if (entry==null)
continue;
if (entry._package)
{
if (name.startsWith(entry._name) || ".".equals(entry._pattern))
return entry._inclusive;
}
else
{
if (name.equals(entry._name))
return entry._inclusive;
if (name.length()>entry._name.length() && '$'==name.charAt(entry._name.length()) && name.startsWith(entry._name))
return entry._inclusive;
}
}
return false;
}
public void addAfter(String afterPattern,String... patterns)
{
if (patterns!=null && afterPattern!=null)
{
ListIterator<String> iter = listIterator();
while (iter.hasNext())
{
String cc=iter.next();
if (afterPattern.equals(cc))
{
for (int i=0;i<patterns.length;i++)
iter.add(patterns[i]);
return;
}
}
}
throw new IllegalArgumentException("after '"+afterPattern+"' not found in "+this);
}
public void addBefore(String beforePattern,String... patterns)
{
if (patterns!=null && beforePattern!=null)
{
ListIterator<String> iter = listIterator();
while (iter.hasNext())
{
String cc=iter.next();
if (beforePattern.equals(cc))
{
iter.previous();
for (int i=0;i<patterns.length;i++)
iter.add(patterns[i]);
return;
}
}
}
throw new IllegalArgumentException("before '"+beforePattern+"' not found in "+this);
}
}