package com.thaiopensource.validate.nvdl; import java.util.StringTokenizer; /** * Stores information about a namespace specification. * A namespace is specified with a namespace pattern and a wildcard. * The wildcard can be present in multiple places in the namespace * specification and each occurence of the wildcard can be replaced with * an arbitrary sequence of characters. * * @author george */ class NamespaceSpecification { /** * Default value for wildcard. */ public static String DEFAULT_WILDCARD = "*"; /** * Constant for any namespace. */ static final String ANY_NAMESPACE = "##any"; /** * The namespace pattern, may contain one or more occurances of the wildcard. */ String ns="\0"; /** * The wildcard character, by default it is *. */ String wildcard = DEFAULT_WILDCARD; /** * Creates a namespace specification from a namespace pattern * using the default wildcard, that is *. * @param ns The namespace pattern */ public NamespaceSpecification(String ns) { this(ns, DEFAULT_WILDCARD); } /** * Creates a namespace specification from a namespace pattern * and a given wildcard. * @param ns The namespace pattern * @param wildcard The given wildcard character. */ public NamespaceSpecification(String ns, String wildcard) { this.ns = ns; this.wildcard = wildcard; } /** * Check if this namespace specification competes with * another namespace specification. * @param other The namespace specification we need to check if * it competes with this namespace specification. * @return true if the namespace specifications compete. */ public boolean compete(NamespaceSpecification other) { // if no wildcard for other then we check coverage if ("".equals(other.wildcard)) { return covers(other.ns); } // split the namespaces at wildcards String[] otherParts = split(other.ns, other.wildcard); // if the given namepsace specification does not use its wildcard // then we just look if the current namespace specification covers it if (otherParts.length == 1) { return covers(other.ns); } // if no wildcard for the current namespace specification if ("".equals(wildcard)) { return other.covers(ns); } // also for the current namespace specification String[] parts = split(ns, wildcard); // now check if the current namespace specification is just an URI if (parts.length == 1) { return other.covers(ns); } // now each namespace specification contains wildcards // suppose we have // ns = a1*a2*...*an // and // other.ns = b1*b2*...*bm // then we only need to check matchPrefix(a1, b1) and matchPrefix(an, bn) where // matchPrefix(a, b) means a starts with b or b starts with a. return matchPrefix(parts[0], otherParts[0]) && matchPrefix(parts[parts.length - 1], otherParts[otherParts.length - 1]); } /** * Checks with either of the strings starts with the other. * @param s1 a String * @param s2 a String * @return true if s1 starts with s2 or s2 starts with s1, false otherwise */ static private boolean matchPrefix(String s1, String s2) { return s1.startsWith(s2) || s2.startsWith(s1); } private String[] split(String value, String wildcard) { StringTokenizer st = new StringTokenizer(value, wildcard, true); int index = st.countTokens(); if (index == 0) return new String[]{value}; String[] parts = new String[index]; index = 0; while (st.hasMoreTokens()) { String token = st.nextToken(); parts[index++] = token.equals(wildcard) ? "" : token; } return parts; } /** * Checks if a namespace specification covers a specified URI. * any namespace pattern covers only the any namespace uri. * @param uri The uri to be checked. * @return true if the namespace pattern covers the specified uri. */ public boolean covers(String uri) { // any namspace covers only the any namespace uri // no wildcard ("") requires equality between namespaces. if (ANY_NAMESPACE.equals(ns) || "".equals(wildcard)) { return ns.equals(uri); } String[] parts = split(ns, wildcard); // no wildcard if (parts.length == 1) { return ns.equals(uri); } // at least one wildcard, we need to check that the start and end are the same // then we get to match a string against a pattern like *p1*...*pn* if (!uri.startsWith(parts[0])) { return false; } if (!uri.endsWith(parts[parts.length - 1])) { return false; } // Check that all remaining parts match the remaining URI. int start = parts[0].length(); int end = uri.length() - parts[parts.length - 1].length(); for (int i = 1; i < parts.length - 1; i++) { if (start > end) { return false; } int match = uri.indexOf(parts[i], start); if (match == -1 || match + parts[i].length() > end) { return false; } start = match + parts[i].length(); } return true; } /** * Checks for equality with another Namespace specification. */ public boolean equals(Object obj) { if (obj instanceof NamespaceSpecification) { NamespaceSpecification other = (NamespaceSpecification)obj; return ns.equals(other.ns) && wildcard.equals(other.wildcard); } return false; } /** * Get a hashcode for this namespace specification. */ public int hashCode() { return (wildcard + "|" + ns).hashCode(); } }