Entry.java

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.codehaus.plexus.classworlds.realm;

/*
 * Copyright 2001-2006 Codehaus Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * Import description entry.
 *
 * @author <a href="mailto:bob@eng.werken.com">bob mcwhirter</a>
 */
class Entry implements Comparable<Entry> {

    final ClassLoader classLoader;

    final String pkgName;

    Entry(ClassLoader realm, String pkgName) {
        this.classLoader = realm;
        this.pkgName = pkgName;
    }

    // ------------------------------------------------------------
    //     Instance methods
    // ------------------------------------------------------------

    /**
     * Retrieve the class loader.
     *
     * @return The class loader.
     */
    ClassLoader getClassLoader() {
        return this.classLoader;
    }

    /**
     * Retrieve the package name.
     *
     * @return The package name.
     */
    String getPackageName() {
        return this.pkgName;
    }

    /**
     * Determine if the class/resource name matches the package
     * described by this entry.
     *
     * @param name The class or resource name to test, must not be <code>null</code>.
     * @return <code>true</code> if this entry matches the
     *         classname, otherwise <code>false</code>.
     */
    boolean matches(String name) {
        String pkg = getPackageName();
        if (pkg.endsWith(".*")) {
            String pkgName;
            if (name.indexOf('/') < 0) {
                // a binary class name, e.g. java.lang.Object
                int index = name.lastIndexOf('.');
                pkgName = (index < 0) ? "" : name.substring(0, index);
            } else {
                // a resource name, e.g. java/lang/Object.class
                int index = name.lastIndexOf('/');
                pkgName = (index < 0) ? "" : name.substring(0, index).replace('/', '.');
            }
            return pkgName.length() == pkg.length() - 2 && pkg.regionMatches(0, pkgName, 0, pkgName.length());
        } else if (pkg.length() > 0) {
            if (name.indexOf('/') < 0) {
                // a binary class name, e.g. java.lang.Object
                if (name.startsWith(pkg)) {
                    if (name.length() == pkg.length()) {
                        // exact match of class name
                        return true;
                    } else if (name.charAt(pkg.length()) == '.') {
                        // prefix match of package name
                        return true;
                    } else if (name.charAt(pkg.length()) == '$') {
                        // prefix match of enclosing type
                        return true;
                    }
                }
            } else {
                // a resource name, e.g. java/lang/Object.class
                if (name.equals(pkg)) {
                    // exact match of resource name
                    return true;
                }
                pkg = pkg.replace('.', '/');
                if (name.startsWith(pkg) && name.length() > pkg.length()) {
                    if (name.charAt(pkg.length()) == '/') {
                        // prefix match of package directory
                        return true;
                    } else if (name.charAt(pkg.length()) == '$') {
                        // prefix match of nested class file
                        return true;
                    } else if (name.length() == pkg.length() + 6 && name.endsWith(".class")) {
                        // exact match of class file
                        return true;
                    }
                }
            }
            return false;
        } else {
            return true;
        }
    }

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    //     java.lang.Comparable
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    /**
     * Compare this entry to another for relative ordering.
     * <p/>
     * <p/>
     * The natural ordering of Entry objects is reverse-alphabetical
     * based upon package name.
     * </p>
     *
     * @param that The object to compare.
     * @return -1 if this object sorts before that object, 0
     *         if they are equal, or 1 if this object sorts
     *         after that object.
     */
    public int compareTo(Entry that) {
        // We are reverse sorting this list, so that
        // we get longer matches first:
        //
        //     com.werken.foo.bar
        //     com.werken.foo
        //     com.werken
        return -(getPackageName().compareTo(that.getPackageName()));
    }

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    //     java.lang.Object
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    /**
     * Test this entry for equality to another.
     * <p/>
     * <p/>
     * Consistent with {@link #compareTo}, this method tests
     * for equality purely on the package name.
     * </p>
     *
     * @param thatObj The object to compare
     * @return <code>true</code> if the two objects are
     *         semantically equivalent, otherwise <code>false</code>.
     */
    public boolean equals(Object thatObj) {
        Entry that = (Entry) thatObj;
        return getPackageName().equals(that.getPackageName());
    }

    /**
     * <p/>
     * Consistent with {@link #equals}, this method creates a hashCode
     * based on the packagename.
     * </p>
     */
    public int hashCode() {
        return getPackageName().hashCode();
    }

    public String toString() {
        return "Entry[import " + getPackageName() + " from realm " + getClassLoader() + "]";
    }
}