--- openide/modules/src/org/openide/modules/Dependency.java +++ openide/modules/src/org/openide/modules/Dependency.java @@ -19,12 +19,15 @@ package org.openide.modules; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; +import java.util.TreeSet; +import org.openide.util.Exceptions; import org.openide.util.Utilities; /** A dependency a module can have. @@ -121,11 +124,23 @@ this.version = (version != null) ? version.intern() : null; } + private static void checkCodeName(String codeName, boolean slashOK) throws IllegalArgumentException { + //This shows up as a hot spot during startup; it should be reasonable + //enough to disable it when assertions are off. + try { + String msg; + assert (msg = validCodeName(codeName, slashOK)) == null : msg; + } catch (AssertionError e) { + Exceptions.printStackTrace (e); + //Compatibility + throw new IllegalArgumentException(e.getMessage()); + } + } + /** 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 { + private static String validCodeName(String codeName, boolean slashOK) { String base; int slash = codeName.indexOf('/'); // NOI18N @@ -133,7 +148,7 @@ base = codeName; } else { if (!slashOK) { - throw new IllegalArgumentException("No slash permitted in: " + codeName); // NOI18N + return "No slash permitted in: " + codeName; // NOI18N } base = codeName.substring(0, slash); @@ -146,51 +161,42 @@ int release = Integer.parseInt(rest); if (release < 0) { - throw new IllegalArgumentException("Negative release number: " + codeName); // NOI18N + return "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 + return "Negative release number: " + codeName; // NOI18N } if (releaseMax <= release) { - throw new IllegalArgumentException("Release number range must be increasing: " + codeName); // NOI18N + return "Release number range must be increasing: " + codeName; // NOI18N } } } catch (NumberFormatException e) { - throw new IllegalArgumentException(e.toString()); + return e.toString(); } } // Now check that the rest is a valid package. - StringTokenizer tok = new StringTokenizer(base, ".", true); // NOI18N + String[] parts = base.split ("\\."); - if ((tok.countTokens() % 2) == 0) { - throw new NumberFormatException("Even number of pieces: " + base); // NOI18N - } + //No idea what this test was doing in the original code, but + //it certainly seems wrong +// if ((parts.length % 2) == 0) { +// return "Even number of pieces: " + base; // NOI18N +// } - boolean expectingPath = true; - - while (tok.hasMoreTokens()) { - if (expectingPath) { - expectingPath = false; - - String nt = tok.nextToken(); - if (!Utilities.isJavaIdentifier(nt) && !"enum".equals (nt)) { // NOI18N - throw new IllegalArgumentException("Bad package component in " + base); // NOI18N + for (String s : parts) { + if (!Utilities.isJavaIdentifier(s) && !"enum".equals (s)) { // NOI18N + return "Bad package component in " + base + " - not a " + + "java identifier: " + s; // NOI18N } - } else { - if (!".".equals(tok.nextToken())) { // NOI18N - throw new NumberFormatException("Expected dot in code name: " + base); // NOI18N } - - expectingPath = true; + return null; } - } - } /** Parse dependencies from tags. * @param type like Dependency.type @@ -202,100 +208,49 @@ if (body == null) { return Collections.emptySet(); } + String[] deps = body.split (",\\s+"); - Set deps = new HashSet(5); + Set result = new HashSet (deps.length); + Map depsByKey = null; - // First split on commas. - StringTokenizer tok = new StringTokenizer(body, ","); // NOI18N + //Create only if assertions enabled + assert (depsByKey = new HashMap()) != null; - if (!tok.hasMoreTokens()) { - throw new IllegalArgumentException("No deps given: \"" + body + "\""); // NOI18N + for (String dep : deps) { + String[] parts = dep.split ("\\s+"); //NOI18N + if (parts.length == 2 && "".equals(parts[0])) { + parts = new String[] { parts[1] }; } - - Map depsByKey = new HashMap(11); - - 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; + if (parts.length % 2 == 0 || parts.length > 3) { + throw new IllegalArgumentException ("Bad dependency syntax: " //NOI18N + + dep + " part count " + parts.length + ": " + + Arrays.asList(parts)); + } String version; - - if (tok2.hasMoreTokens()) { - String compthing = tok2.nextToken(); - - if (compthing.equals(">")) { // NOI18N - comparison = Dependency.COMPARE_SPEC; - } else if (compthing.equals("=")) { // NOI18N + if (parts.length == 3) { + if ("=".equals(parts[1])) { comparison = Dependency.COMPARE_IMPL; + } else if (">".equals(parts[1])) { + comparison = Dependency.COMPARE_SPEC; + } else if ("".equals(parts[1])){ + comparison = Dependency.COMPARE_ANY; } else { - throw new IllegalArgumentException("Strange comparison string: " + compthing); // NOI18N + throw new IllegalArgumentException ("Unrecognized comparison" + //NOI18N + " string in " + dep + ":'" + parts[1] + "'"); //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()); - } - } + version = parts[2]; } else { comparison = Dependency.COMPARE_ANY; version = null; } - - if (type == Dependency.TYPE_MODULE) { + String name = parts[0]; + switch (type) { + case 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) { + assertRangedComparison (name, comparison, dep); + break; + case 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 } @@ -303,19 +258,17 @@ if (comparison == Dependency.COMPARE_ANY) { throw new IllegalArgumentException("Must give a comparison for a Java dep: " + body); // NOI18N } - } else if (type == Dependency.TYPE_IDE) { + break; + case 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); @@ -323,7 +276,6 @@ ok = false; } } - if (!ok) { throw new IllegalArgumentException("Invalid IDE dependency: " + name); // NOI18N } @@ -332,44 +284,86 @@ 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 + break; + case Dependency.TYPE_PACKAGE : + int idx = name.indexOf('['); + + if (idx != -1) { + if (idx > 0) { + checkCodeName(name.substring(0, idx), false); } - checkCodeName(name, false); - } else if (type == Dependency.TYPE_NEEDS) { - if (comparison != Dependency.COMPARE_ANY) { - throw new IllegalArgumentException("Cannot give a comparison for a token needs dep: " + body); // NOI18N + 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); - } else if (type == Dependency.TYPE_RECOMMENDS) { - if (comparison != Dependency.COMPARE_ANY) { - throw new IllegalArgumentException("Cannot give a comparison for a token needs dep: " + body); // NOI18N } - checkCodeName(name, false); - } else { - throw new IllegalArgumentException("unknown type"); // NOI18N + 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 + } + break; + case Dependency.TYPE_RECOMMENDS : + case Dependency.TYPE_REQUIRES : + case Dependency.TYPE_NEEDS : + if (comparison != Dependency.COMPARE_ANY) { + throw new IllegalArgumentException("Cannot " + + "give a comparison for this type of " + + "manifest entry: " + body); // NOI18N + } + checkCodeName(name, false); + break; + default : + throw new AssertionError ("" + type); + } Dependency nue = new Dependency(type, name, comparison, version); + if (depsByKey != null) { 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); } + depsByKey.put (key, nue); } + result.add (nue); + } + return result; + } - return deps; + private static void assertRangedComparison (String name, int comparison, String dep) { + //Don't do this check with assertions off + String err = checkRangedComparison (name, comparison, dep); + try { + assert err == null : err; + } catch (AssertionError e) { + throw new IllegalArgumentException (e.getMessage()); } + } + private static String checkRangedComparison (String name, int comparison, String dep) { + if ((name.indexOf('-') != -1) && (comparison == Dependency.COMPARE_IMPL)) { + return "Cannot have an implementation dependency on a " + + "ranged release version: " + dep; //NOI18N + } + return null; + } + /** Get the type. */ public final int getType() { return type; @@ -524,8 +518,6 @@ break; } - - //System.err.println("Key for " + d + " is " + this); } public int hashCode() { --- openide/modules/src/org/openide/modules/SpecificationVersion.java +++ openide/modules/src/org/openide/modules/SpecificationVersion.java @@ -22,6 +22,7 @@ // 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.io.Serializable; import java.util.*; @@ -29,7 +30,7 @@ * @author Jesse Glick * @since 1.24 */ -public final class SpecificationVersion implements Comparable { +public final class SpecificationVersion implements Comparable, Serializable { // Might be a bit wasteful of memory, but many SV's are created during // startup, so best to not have to reparse them each time! // In fact sharing the int arrays might save a bit of memory overall,