alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2002 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.openide.modules;

// THIS CLASS OUGHT NOT USE NbBundle NOR org.openide CLASSES
// OUTSIDE OF openide-util.jar! UI AND FILESYSTEM/DATASYSTEM
// INTERACTIONS SHOULD GO ELSEWHERE.

import java.util.*;
import org.openide.util.Utilities;

/** A dependency a module can have.
 * @author Jesse Glick
 * @since 1.24
 */
public final class Dependency {
    /** Dependency on another module. */
    public final static int TYPE_MODULE = 1;
    /** Dependency on a package. */
    public final static int TYPE_PACKAGE = 2;
    /** Dependency on Java. */
    public final static int TYPE_JAVA = 3;
    /** Dependency on the IDE. */
    public final static int TYPE_IDE = 4;
    /** Dependency on a token.
     * @see ModuleInfo#getProvides
     * @since 2.3
     */
    public final static int TYPE_REQUIRES = 5;
    /** Comparison by specification version. */
    public final static int COMPARE_SPEC = 1;
    /** Comparison by implementation version. */
    public final static int COMPARE_IMPL = 2;
    /** No comparison, just require the dependency to be present. */
    public final static int COMPARE_ANY = 3;

    /** Name, for purposes of dependencies, of the IDE. */
    public static final String IDE_NAME = System.getProperty("org.openide.major.version", "IDE"); // NOI18N
    /** Specification version of the IDE. */
    public static final SpecificationVersion IDE_SPEC = makeSpec(System.getProperty("org.openide.specification.version")); // NOI18N
    /** Implementation version of the IDE. */
    public static final String IDE_IMPL = System.getProperty("org.openide.version"); // NOI18N
    /** Name, for purposes of dependencies, of the Java platform. */
    public static final String JAVA_NAME = "Java"; // NOI18N
    /** Specification version of the Java platform. */
    public static final SpecificationVersion JAVA_SPEC = makeSpec(System.getProperty("java.specification.version")); // NOI18N
    /** Implementation version of the Java platform. */
    public static final String JAVA_IMPL = System.getProperty("java.version"); // NOI18N
    /** Name, for purposes of dependencies, of the Java VM. */
    public static final String VM_NAME = "VM"; // NOI18N
    /** Specification version of the Java VM. */
    public static final SpecificationVersion VM_SPEC = makeSpec(System.getProperty("java.vm.specification.version")); // NOI18N
    /** Implementation version of the Java VM. */
    public static final String VM_IMPL = System.getProperty("java.vm.version"); // NOI18N
    

    private final int type, comparison;
    private final String name, version;

    private Dependency(int type, String name, int comparison, String version) {
        this.type = type;
        this.name = name.intern();
        this.comparison = comparison;
        this.version = (version != null) ? version.intern() : null;
    }

    /** Verify the format of a code name.
     * Caller specifies whether a slash plus release version is permitted in this context.
     */
    private static void checkCodeName(String codeName, boolean slashOK) throws IllegalArgumentException {
        String base;
        int slash = codeName.indexOf('/'); // NOI18N
        if (slash == -1) {
            base = codeName;
        } else {
            if (! slashOK) {
                throw new IllegalArgumentException("No slash permitted in: " + codeName); // NOI18N
            }
            base = codeName.substring(0, slash);
            String rest = codeName.substring(slash + 1);
            int dash = rest.indexOf('-'); // NOI18N
            try {
                if (dash == -1) {
                    int release = Integer.parseInt(rest);
                    if (release < 0) throw new IllegalArgumentException("Negative release number: " + codeName); // NOI18N
                } else {
                    int release = Integer.parseInt(rest.substring(0, dash));
                    int releaseMax = Integer.parseInt(rest.substring(dash + 1));
                    if (release < 0) throw new IllegalArgumentException("Negative release number: " + codeName); // NOI18N
                    if (releaseMax <= release) throw new IllegalArgumentException("Release number range must be increasing: " + codeName); // NOI18N
                }
            } catch (NumberFormatException e) {
                throw new IllegalArgumentException(e.toString());
            }
        }
        // Now check that the rest is a valid package.
        StringTokenizer tok = new StringTokenizer(base, ".", true); // NOI18N
        if (tok.countTokens() % 2 == 0) {
            throw new NumberFormatException("Even number of pieces: " + base); // NOI18N
        }
        boolean expectingPath = true;
        while (tok.hasMoreTokens()) {
            if (expectingPath) {
                expectingPath = false;
                if (! Utilities.isJavaIdentifier(tok.nextToken())) {
                    throw new IllegalArgumentException("Bad package component in " + base); // NOI18N
                }
            } else {
                if (! ".".equals(tok.nextToken())) { // NOI18N
                    throw new NumberFormatException("Expected dot in code name: " + base); // NOI18N
                }
                expectingPath = true;
            }
        }
    }

    /** Parse dependencies from tags.
    * @param type like Dependency.type
    * @param body actual text of tag body; if null, returns nothing
    * @return a set of dependencies
    * @throws IllegalArgumentException if they are malformed or inconsistent
    */
    public static Set create(int type, String body) throws IllegalArgumentException {
        if (body == null) {
            return Collections.EMPTY_SET;
        }
        Set deps = new HashSet(5); // Set
        // First split on commas.
        StringTokenizer tok = new StringTokenizer(body, ","); // NOI18N
        if (! tok.hasMoreTokens()) {
            throw new IllegalArgumentException("No deps given: \"" + body + "\""); // NOI18N
        }
        Map depsByKey = new HashMap(10); // Map
        while (tok.hasMoreTokens()) {
            String onedep = tok.nextToken();
            StringTokenizer tok2 = new StringTokenizer(onedep, " \t\n\r"); // NOI18N
            if (! tok2.hasMoreTokens()) {
                throw new IllegalArgumentException("No name in dependency: " + onedep); // NOI18N
            }
            String name = tok2.nextToken();
            int comparison;
            String version;
            if (tok2.hasMoreTokens()) {
                String compthing = tok2.nextToken();
                if (compthing.equals(">")) { // NOI18N
                    comparison = Dependency.COMPARE_SPEC;
                } else if (compthing.equals("=")) { // NOI18N
                    comparison = Dependency.COMPARE_IMPL;
                } else {
                    throw new IllegalArgumentException("Strange comparison string: " + compthing); // NOI18N
                }
                if (! tok2.hasMoreTokens()) {
                    throw new IllegalArgumentException("Comparison string without version: " + onedep); // NOI18N
                }
                version = tok2.nextToken();
                if (tok2.hasMoreTokens()) {
                    throw new IllegalArgumentException("Trailing garbage in dependency: " + onedep); // NOI18N
                }
                if (comparison == Dependency.COMPARE_SPEC) {
                    try {
                        new SpecificationVersion(version);
                    } catch (NumberFormatException nfe) {
                        throw new IllegalArgumentException(nfe.toString());
                    }
                }
            } else {
                comparison = Dependency.COMPARE_ANY;
                version = null;
            }
            if (type == Dependency.TYPE_MODULE) {
                checkCodeName (name, true);
                if (name.indexOf('-') != -1 && comparison == Dependency.COMPARE_IMPL) {
                    throw new IllegalArgumentException("Cannot have an implementation dependency on a ranged release version: " + onedep); // NOI18N
                }
            } else if (type == Dependency.TYPE_PACKAGE) {
                int idx = name.indexOf('[');
                if (idx != -1) {
                    if (idx > 0) {
                        checkCodeName(name.substring(0, idx), false);
                    }
                    if (name.charAt(name.length() - 1) != ']') {
                        throw new IllegalArgumentException("No close bracket on package dep: " + name); // NOI18N
                    }
                    checkCodeName(name.substring(idx + 1, name.length() - 1), false);
                } else {
                    checkCodeName(name, false);
                }
                if (idx == 0 && comparison != Dependency.COMPARE_ANY) {
                    throw new IllegalArgumentException("Cannot use a version comparison on a package dependency when only a sample class is given"); // NOI18N
                }
                if (idx > 0 && name.substring(idx + 1, name.length() - 1).indexOf('.') != -1) {
                    throw new IllegalArgumentException("Cannot have a sample class with dots when package is specified"); // NOI18N
                }
            } else if (type == Dependency.TYPE_JAVA) {
                if (! (name.equals(JAVA_NAME) || name.equals(VM_NAME))) {// NOI18N
                    throw new IllegalArgumentException("Java dependency must be on \"Java\" or \"VM\": " + name); // NOI18N
                }
                if (comparison == Dependency.COMPARE_ANY) {
                    throw new IllegalArgumentException("Must give a comparison for a Java dep: " + body); // NOI18N
                }
            } else if (type == Dependency.TYPE_IDE) {
                if (! (name.equals ("IDE"))) { // NOI18N
                    int slash = name.indexOf ("/"); // NOI18N
                    boolean ok;
                    if (slash == -1) {
                        ok = false;
                    } else {
                        if (! name.substring(0, slash).equals("IDE")) { // NOI18N
                            ok = false;
                        }
                        try {
                            int v = Integer.parseInt (name.substring (slash + 1));
                            ok = (v >= 0);
                        } catch (NumberFormatException e) {
                            ok = false;
                        }
                    }
                    if (! ok) {
                        throw new IllegalArgumentException("Invalid IDE dependency: " + name); // NOI18N
                    }
                }
                if (comparison == Dependency.COMPARE_ANY) {
                    throw new IllegalArgumentException("Must give a comparison for an IDE dep: " + body); // NOI18N
                }
            } else if (type == Dependency.TYPE_REQUIRES) {
                if (comparison != Dependency.COMPARE_ANY) {
                    throw new IllegalArgumentException("Cannot give a comparison for a token requires dep: " + body); // NOI18N
                }
                checkCodeName(name, false);
            } else {
                throw new IllegalArgumentException("unknown type"); // NOI18N
            }
            Dependency nue = new Dependency(type, name, comparison, version);
            DependencyKey key = new DependencyKey(nue);
            if (depsByKey.containsKey(key)) {
                throw new IllegalArgumentException("Dependency " + nue + " duplicates the similar dependency " + depsByKey.get(key)); // NOI18N
            } else {
                deps.add(nue);
                depsByKey.put(key, nue);
            }
        }
        return deps;
    }

    /** Key for checking for duplicates among dependencies.
     * The unique characteristics of a dependency are:
     * 1. The basic name. No release versions, no sample classes for packages
     * (though if you specify only the class and not the package, this is different).
     * 2. The type of dependency (module, package, etc.).
     * Sample things which ought not be duplicated:
     * 1. Sample classes within a package.
     * 2. The same module with different release versions (use ranged releases as needed).
     * 3. Impl & spec comparisons (the impl comparison is stricter anyway).
     * 4. Different versions of the same thing (makes no sense).
     */
    private static final class DependencyKey {
        private final int type;
        private final String name;
        public DependencyKey(Dependency d) {
            type = d.getType();
            switch (type) {
            case TYPE_MODULE:
            case TYPE_IDE:
                String codeName = d.getName();
                int idx = codeName.lastIndexOf('/');
                if (idx == -1) {
                    name = codeName;
                } else {
                    name = codeName.substring(0, idx);
                }
                break;
            case TYPE_PACKAGE:
                String pkgName = d.getName();
                idx = pkgName.indexOf('[');
                if (idx != -1) {
                    if (idx == 0) {
                        // [org.apache.jasper.Constants]
                        // Keep the [] only to differentiate it from a package name:
                        name = pkgName;
                    } else {
                        // org.apache.jasper[Constants]
                        name = pkgName.substring(0, idx);
                    }
                } else {
                    // org.apache.jasper
                    name = pkgName;
                }
                break;
            default:
                // TYPE_REQUIRES, TYPE_JAVA
                name = d.getName();
                break;
            }
            //System.err.println("Key for " + d + " is " + this);
        }
        public int hashCode() {
            return name.hashCode();
        }
        public boolean equals(Object o) {
            return (o instanceof DependencyKey) &&
                ((DependencyKey)o).name.equals(name) &&
                ((DependencyKey)o).type == type;
        }
        public String toString() {
            return "DependencyKey[" + name + "," + type + "]"; // NOI18N
        }
    }

    /** Get the type. */
    public final int getType() {
        return type;
    }

    /** Get the name of the depended-on object. */
    public final String getName() {
        return name;
    }

    /** Get the comparison type. */
    public final int getComparison() {
        return comparison;
    }

    /** Get the version to compare against (or null). */
    public final String getVersion() {
        return version;
    }

    /** Overridden to compare contents. */
    public boolean equals(Object o) {
        if (o.getClass() != Dependency.class) return false;
        Dependency d = (Dependency) o;
        return type == d.type &&
               comparison == d.comparison &&
               name.equals(d.name) &&
               Utilities.compareObjects(version, d.version);
    }

    /** Overridden to hash by contents. */
    public int hashCode() {
        return 772067 ^ type ^ name.hashCode();
    }
    
    /** Unspecified string representation for debugging. */
    public String toString() {
        StringBuffer buf = new StringBuffer(100);
        if (type == TYPE_MODULE) {
            buf.append("module "); // NOI18N
        } else if (type == TYPE_PACKAGE) {
            buf.append("package "); // NOI18N
        } else if (type == TYPE_REQUIRES) {
            buf.append("token "); // NOI18N
        }
        buf.append(name);
        if (comparison == COMPARE_IMPL) {
            buf.append(" = "); // NOI18N
            buf.append(version);
        } else if (comparison == COMPARE_SPEC) {
            buf.append(" > "); // NOI18N
            buf.append(version);
        }
        return buf.toString();
    }
    
    /** Try to make a specification version from a string.
     * Deal with errors gracefully and try to recover something from it.
     * E.g. "1.4.0beta" is technically erroneous; correct to "1.4.0".
     */
    private static SpecificationVersion makeSpec(String vers) {
        if (vers != null) {
            try {
                return new SpecificationVersion(vers);
            } catch (NumberFormatException nfe) {
                System.err.println("WARNING: invalid specification version: " + vers); // NOI18N
            }
            do {
                vers = vers.substring(0, vers.length() - 1);
                try {
                    return new SpecificationVersion(vers);
                } catch (NumberFormatException nfe) {
                    // ignore
                }
            } while (vers.length() > 0);
        }
        // Nothing decent in it at all; use zero.
        return new SpecificationVersion("0"); // NOI18N
    }
    
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.