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 }