001 package com.oxygenxml.validate.nvdl; 002 003 import java.util.ArrayList; 004 import java.util.Enumeration; 005 import java.util.Hashtable; 006 import java.util.Iterator; 007 import java.util.List; 008 009 import org.xml.sax.Locator; 010 import org.xml.sax.helpers.LocatorImpl; 011 012 class Mode { 013 static final int ATTRIBUTE_PROCESSING_NONE = 0; 014 static final int ATTRIBUTE_PROCESSING_QUALIFIED = 1; 015 static final int ATTRIBUTE_PROCESSING_FULL = 2; 016 017 /** 018 * A special mode. In a mode usage this will be 019 * resolved by the mode usage to the actual current mode 020 * from that mode usage. 021 */ 022 static final Mode CURRENT = new Mode("#current", null); 023 024 /** 025 * Mode name prefix used for inline anonymous modes. 026 */ 027 static final String ANONYMOUS_MODE_NAME_PREFIX = "#anonymous#"; 028 029 /** 030 * Inline anonymous modes counter. 031 */ 032 static int anonymousModeCounter = 0; 033 034 /** 035 * The mode name. 036 */ 037 private final String name; 038 039 /** 040 * The base mode. 041 */ 042 private Mode baseMode; 043 044 /** 045 * Flag indicating if this mode is defined by the user 046 * or is an automatically generated mode. 047 */ 048 private boolean defined; 049 /** 050 * Locate the place where this mode is defined. 051 */ 052 private Locator whereDefined; 053 054 /** 055 * Locate the place this mode is first used. 056 * Useful to report with location errors like 057 * 'Mode "xxx" not defined'. 058 */ 059 private Locator whereUsed; 060 061 062 private final Hashtable elementMap = new Hashtable(); 063 private final Hashtable attributeMap = new Hashtable(); 064 private int attributeProcessing = -1; 065 066 private final Hashtable nssElementMap = new Hashtable(); 067 private final Hashtable nssAttributeMap = new Hashtable(); 068 069 /** 070 * List with included modes. 071 */ 072 private List includedModes = new ArrayList(); 073 074 void addIncludedMode(Mode mode) { 075 includedModes.add(mode); 076 } 077 078 /** 079 * Creates a mode extending a base mode. 080 * @param name The new mode name. 081 * @param baseMode The base mode. 082 */ 083 Mode(String name, Mode baseMode) { 084 this.name = name; 085 this.baseMode = baseMode; 086 } 087 088 /** 089 * Creates an anonymous mode. 090 * @param baseMode 091 */ 092 public Mode(Mode baseMode) { 093 this(ANONYMOUS_MODE_NAME_PREFIX+anonymousModeCounter++, baseMode); 094 } 095 096 /** 097 * Get this mode name. 098 * @return The name. 099 */ 100 String getName() { 101 return name; 102 } 103 104 /** 105 * Get the base mode. 106 * @return The base mode. 107 */ 108 Mode getBaseMode() { 109 return baseMode; 110 } 111 112 /** 113 * Set a base mode. 114 * @param baseMode The new base mode. 115 */ 116 void setBaseMode(Mode baseMode) { 117 this.baseMode = baseMode; 118 } 119 120 /** 121 * Get the set of element actions for a given namespace. 122 * If this mode has an explicit handling of that namespace then we get those 123 * actions, otherwise we get the actions for any namespace. 124 * @param ns The namespace we look for element actions for. 125 * @return A set of element actions. 126 */ 127 ActionSet getElementActions(String ns) { 128 ActionSet actions = getElementActionsExplicit(ns); 129 if (actions == null) { 130 actions = getElementActionsExplicit(NamespaceSpecification.ANY_NAMESPACE); 131 // this is not correct: it breaks a derived mode that use anyNamespace 132 // elementMap.put(ns, actions); 133 } 134 return actions; 135 } 136 137 /** 138 * Look for element actions specifically specified 139 * for this namespace. If the current mode does not have 140 * actions for that namespace look at base modes. If the actions 141 * are defined in a base mode we need to get a copy of those actions 142 * associated with this mode, so we call changeCurrentMode on them. 143 * 144 * @param ns The namespace 145 * @return A set of element actions. 146 */ 147 private ActionSet getElementActionsExplicit(String ns) { 148 ActionSet actions = (ActionSet)elementMap.get(ns); 149 if (actions==null) { 150 // iterate namespace specifications. 151 for (Enumeration e = nssElementMap.keys(); e.hasMoreElements() && actions==null;) { 152 NamespaceSpecification nssI = (NamespaceSpecification)e.nextElement(); 153 // If a namespace specification convers the current namespace URI then we get those actions. 154 if (nssI.covers(ns)) { 155 actions = (ActionSet)nssElementMap.get(nssI); 156 } 157 } 158 // Store them in the element Map for faster access next time. 159 if (actions!=null) { 160 elementMap.put(ns, actions); 161 } 162 } 163 // Look into the included modes 164 if (actions == null && includedModes != null) { 165 Iterator i = includedModes.iterator(); 166 while (actions == null && i.hasNext()) { 167 Mode includedMode = (Mode)i.next(); 168 actions = includedMode.getElementActionsExplicit(ns); 169 } 170 if (actions != null) { 171 actions = actions.changeCurrentMode(this); 172 elementMap.put(ns, actions); 173 } 174 } 175 176 if (actions!=null && actions.getCancelNestedActions()) { 177 actions = null; 178 } 179 180 // No actions specified, look into the base mode. 181 if (actions == null && baseMode != null) { 182 actions = baseMode.getElementActionsExplicit(ns); 183 if (actions != null) { 184 actions = actions.changeCurrentMode(this); 185 elementMap.put(ns, actions); 186 } 187 } 188 189 return actions; 190 } 191 192 /** 193 * Get the set of attribute actions for a given namespace. 194 * If this mode has an explicit handling of that namespace then we get those 195 * actions, otherwise we get the actions for any namespace. 196 * @param ns The namespace we look for attribute actions for. 197 * @return A set of attribute actions. 198 */ 199 AttributeActionSet getAttributeActions(String ns) { 200 AttributeActionSet actions = getAttributeActionsExplicit(ns); 201 if (actions == null) { 202 actions = getAttributeActionsExplicit(NamespaceSpecification.ANY_NAMESPACE); 203 // this is not correct: it breaks a derived mode that use anyNamespace 204 // attributeMap.put(ns, actions); 205 } 206 return actions; 207 } 208 209 /** 210 * Look for attribute actions specifically specified 211 * for this namespace. If the current mode does not have 212 * actions for that namespace look at base modes. If the actions 213 * are defined in a base mode we need to get a copy of those actions 214 * associated with this mode, so we call changeCurrentMode on them. 215 * 216 * @param ns The namespace 217 * @return A set of attribute actions. 218 */ 219 private AttributeActionSet getAttributeActionsExplicit(String ns) { 220 AttributeActionSet actions = (AttributeActionSet)attributeMap.get(ns); 221 if (actions==null) { 222 // iterate namespace specifications. 223 for (Enumeration e = nssAttributeMap.keys(); e.hasMoreElements() && actions==null;) { 224 NamespaceSpecification nssI = (NamespaceSpecification)e.nextElement(); 225 // If a namespace specification convers the current namespace URI then we get those actions. 226 if (nssI.covers(ns)) { 227 actions = (AttributeActionSet)nssAttributeMap.get(nssI); 228 } 229 } 230 // Store them in the element Map for faster access next time. 231 if (actions!=null) { 232 attributeMap.put(ns, actions); 233 } 234 } 235 236 if (actions!=null && actions.getCancelNestedActions()) { 237 actions = null; 238 } 239 240 if (actions == null && baseMode != null) { 241 actions = baseMode.getAttributeActionsExplicit(ns); 242 if (actions != null) 243 attributeMap.put(ns, actions); 244 } 245 return actions; 246 } 247 248 /** 249 * Computes (if not already computed) the attributeProcessing 250 * for this mode and returns it. 251 * If it find anything different than attach then we need to perform 252 * attribute processing. 253 * If only attributes for a specific namespace have actions then we only need to 254 * process qualified attributes, otherwise we need to process all attributes. 255 * 256 * @return The attribute processing for this mode. 257 */ 258 int getAttributeProcessing() { 259 if (attributeProcessing == -1) { 260 if (baseMode != null) 261 attributeProcessing = baseMode.getAttributeProcessing(); 262 else 263 attributeProcessing = ATTRIBUTE_PROCESSING_NONE; 264 for (Enumeration en = nssAttributeMap.keys(); en.hasMoreElements() && attributeProcessing != ATTRIBUTE_PROCESSING_FULL;) { 265 NamespaceSpecification nss = (NamespaceSpecification)en.nextElement(); 266 AttributeActionSet actions = (AttributeActionSet)nssAttributeMap.get(nss); 267 if (!actions.getAttach() 268 || actions.getReject() 269 || actions.getSchemas().length > 0) 270 attributeProcessing = ((nss.ns.equals("") || nss.ns.equals(NamespaceSpecification.ANY_NAMESPACE)) 271 ? ATTRIBUTE_PROCESSING_FULL 272 : ATTRIBUTE_PROCESSING_QUALIFIED); 273 } 274 } 275 return attributeProcessing; 276 } 277 278 /** 279 * Get the locator that points to the place the 280 * mode is defined. 281 * @return a locator. 282 */ 283 Locator getWhereDefined() { 284 return whereDefined; 285 } 286 287 /** 288 * Getter for the defined flag. 289 * @return defined. 290 */ 291 boolean isDefined() { 292 return defined; 293 } 294 295 boolean isAnonymous() { 296 return name.startsWith(ANONYMOUS_MODE_NAME_PREFIX); 297 } 298 299 /** 300 * Get a locator pointing to the first place this mode is used. 301 * @return a locator. 302 */ 303 Locator getWhereUsed() { 304 return whereUsed; 305 } 306 307 /** 308 * Record the locator if this is the first location this mode is used. 309 * @param locator Points to the location this mode is used from. 310 */ 311 void noteUsed(Locator locator) { 312 if (whereUsed == null && locator != null) 313 whereUsed = new LocatorImpl(locator); 314 } 315 316 /** 317 * Record the locator this mode is defined at. 318 * @param locator Points to the mode definition. 319 */ 320 void noteDefined(Locator locator) { 321 defined = true; 322 if (whereDefined == null && locator != null) 323 whereDefined = new LocatorImpl(locator); 324 } 325 326 /** 327 * Adds a set of element actions to be performed in this mode 328 * for elements in a specified namespace. 329 * 330 * @param ns The namespace pattern. 331 * @param wildcard The wildcard character. 332 * @param actions The set of element actions. 333 * @return true if successfully added, that is the namespace was 334 * not already present in the elementMap, otherwise false, the 335 * caller should signal a script error in this case. 336 */ 337 boolean bindElement(String ns, String wildcard, ActionSet actions) { 338 NamespaceSpecification nss = new NamespaceSpecification(ns, wildcard); 339 if (nssElementMap.get(nss) != null) 340 return false; 341 for (Enumeration e = nssElementMap.keys(); e.hasMoreElements();) { 342 NamespaceSpecification nssI = (NamespaceSpecification)e.nextElement(); 343 if (nss.compete(nssI)) { 344 return false; 345 } 346 } 347 nssElementMap.put(nss, actions); 348 return true; 349 } 350 351 /** 352 * Adds a set of attribute actions to be performed in this mode 353 * for attributes in a specified namespace. 354 * 355 * @param ns The namespace pattern. 356 * @param wildcard The wildcard character. 357 * @param actions The set of attribute actions. 358 * @return true if successfully added, that is the namespace was 359 * not already present in the attributeMap, otherwise false, the 360 * caller should signal a script error in this case. 361 */ 362 boolean bindAttribute(String ns, String wildcard, AttributeActionSet actions) { 363 NamespaceSpecification nss = new NamespaceSpecification(ns, wildcard); 364 if (nssAttributeMap.get(nss) != null) 365 return false; 366 for (Enumeration e = nssAttributeMap.keys(); e.hasMoreElements();) { 367 NamespaceSpecification nssI = (NamespaceSpecification)e.nextElement(); 368 if (nss.compete(nssI)) { 369 return false; 370 } 371 } 372 nssAttributeMap.put(nss, actions); 373 return true; 374 } 375 }