001    package com.oxygenxml.validate.nvdl;
002    
003    import java.util.regex.PatternSyntaxException;
004    
005    /**
006     * Stores information about a namespace specification.
007     * A namespace is specified with a namespace pattern and a wildcard.
008     * The wildcard can be present in multiple places in the namespace 
009     * specification and each occurence of the wildcard can be replaced with
010     * an arbitrary sequence of characters.
011     * 
012     * @author george
013     */
014    class NamespaceSpecification {
015      /**
016       * Default value for wildcard.
017       */
018      public static String DEFAULT_WILDCARD = "*";
019      
020      /**
021       * Constant for any namespace.
022       */
023      static final String ANY_NAMESPACE = "##any";
024      
025      
026      /**
027       * The namespace pattern, may contain one or more occurances of the wildcard.
028       */
029      String ns="\0";
030      
031      /**
032       * The wildcard character, by default it is *.
033       */
034      String wildcard = DEFAULT_WILDCARD;
035      
036      
037      /**
038       * Creates a namespace specification from a namespace pattern
039       * using the default wildcard, that is *.
040       * @param ns The namespace pattern
041       */
042      public NamespaceSpecification(String ns) {
043        this(ns, DEFAULT_WILDCARD);
044      }
045      
046      /**
047       * Creates a namespace specification from a namespace pattern
048       * and a given wildcard.
049       * @param ns The namespace pattern
050       * @param wildcard The given wildcard character.
051       */
052      public NamespaceSpecification(String ns, String wildcard) {
053        this.ns = ns;
054        this.wildcard = wildcard;
055      }
056    
057      /**
058       * Check if this namespace specification competes with 
059       * another namespace specification.
060       * @param n The namespace specification we need to check if 
061       * it competes with this namespace specification.
062       * @return true if the namespace specifications compete.
063       */
064      public boolean compete(NamespaceSpecification n) {
065        // if no wildcard for n then we check coverage
066        if ("".equals(n.wildcard)) {
067          return covers(n.ns);
068        }
069        // split the namespaces at wildcards     
070        String[] nParts = split(n.ns, n.wildcard);
071        
072        // if the given namepsace specification does not use its wildcard
073        // then we just look if the current namespace specification covers it
074        if (nParts.length ==1) {
075          return covers(n.ns);
076        }
077        // if no wildcard for the current namespace specification
078        if ("".equals(wildcard)) {
079          return n.covers(ns);
080        }
081        // also for the current namespace specification
082        String[] parts = split(ns, wildcard); 
083        // now check if the current namespace specification is just an URI
084        if (parts.length ==1) {
085          return n.covers(ns);
086        }
087        // now each namespace specification contains wildcards
088        // suppose we have 
089        // ns   = a1*a2*...*an 
090        // and 
091        // n.ns = b1*b2*...*bm
092        // then we only need to check match(a1, b1) and match (an, bn) where
093        // match(a,b) in this case means a starts with b or b starts with a. 
094        return 
095        (parts[0].startsWith(nParts[0]) || nParts[0].startsWith(parts[0]))&&
096        (   parts[parts.length-1].startsWith(nParts[nParts.length-1]) || 
097            nParts[nParts.length-1].startsWith(parts[parts.length-1])
098         );   
099      }
100    
101      private String[] split(String value, String regexp) {
102        String[] parts = null;
103        try {
104          parts = value.split("\\" + regexp, -1);
105        } catch (PatternSyntaxException e) {
106          try {
107            parts = value.split(regexp, -1);
108          } catch (PatternSyntaxException e2) {
109            parts = new String[]{value};
110          }
111        }
112        return parts;
113      }
114    
115      /**
116       * Checks if a namespace specification covers a specified URI.
117       * any namespace pattern covers only the any namespace uri. 
118       * @param uri The uri to be checked.
119       * @return true if the namespace pattern covers the specified uri.
120       */
121      public boolean covers(String uri) {
122        // any namspace covers only the any namespace uri
123        // no wildcard ("") requires equality between namespaces.
124        if (ANY_NAMESPACE.equals(ns) || "".equals(wildcard)) {
125          return ns.equals(uri);
126        }
127        
128        
129        String[] parts = split(ns, wildcard);    
130        // no wildcard
131        if (parts.length == 1) {
132          return ns.equals(uri);
133        }
134        // at least one wildcard, we need to check that the start and end are the same
135        // then we get to match a string against a pattern like *p1*...*pn*
136        if (!uri.startsWith(parts[0])) {
137          return false;
138        }
139        if (!uri.endsWith(parts[parts.length-1])) {
140          return false;
141        }
142        // Check that all remaining parts match the remaining URI.
143        int start = parts[0].length();
144        int end = uri.length() - parts[parts.length-1].length();
145        for (int i=1; i<parts.length-1; i++) {
146          if (start > end) {
147            return false;
148          }
149          int match = uri.indexOf(parts[i], start);
150          if (match==-1 || match+parts[i].length()>end) {
151            return false;
152          }
153          start = match + parts[i].length();
154        }    
155        return true;
156      }
157    
158      /**
159       * Checks for equality with another Namespace specification.
160       */
161      public boolean equals(Object obj) {
162        if (obj instanceof NamespaceSpecification) {
163          NamespaceSpecification other = (NamespaceSpecification)obj;
164          return ns.equals(other.ns) && wildcard.equals(other.wildcard);
165        }    
166        return false;
167      }
168      
169      /**
170       * Get a hashcode for this namespace specification.
171       */
172      public int hashCode() {
173        return (wildcard+"|"+ns).hashCode();
174      }
175    }