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 }