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 }