001    package com.oxygenxml.validate.nvdl;
002    
003    import com.thaiopensource.util.Equal;
004    
005    import java.util.Vector;
006    import java.util.Enumeration;
007    
008    /**
009     * Stores mode usage information.
010     */
011    class ModeUsage {
012      /**
013       * The use mode.
014       */
015      private Mode mode;
016      /**
017       * The current mode used until now.
018       */
019      final Mode currentMode;
020      
021      /**
022       * Modes depending on context.
023       */
024      private ContextMap modeMap;
025      
026      /**
027       * 
028       */
029      private int attributeProcessing = -1;
030    
031      /**
032       * Creates a use mode.
033       * @param mode The mode to be used.
034       * @param currentMode The mode used until the new mode.
035       */
036      ModeUsage(Mode mode, Mode currentMode) {
037        this(mode, currentMode, null);
038      }
039    
040      /**
041       * Creates a use mode.
042       * @param mode The mode to be used.
043       * @param currentMode The mode used until now.
044       * @param modeMap Modes to be used depending on context.
045       */
046      private ModeUsage(Mode mode, Mode currentMode, ContextMap modeMap) {
047        this.mode = mode;
048        this.currentMode = currentMode;
049        this.modeMap = modeMap;
050      }
051    
052      /**
053       * Gets a new mode usage with a different current mode
054       * but with the same mode and modeMap as this one.
055       * @param currentMode The new current mode.
056       * @return A new mode usage with the changed current mode.
057       */
058      ModeUsage changeCurrentMode(Mode currentMode) {
059        return new ModeUsage(mode, currentMode, modeMap);
060      }
061    
062      /**
063       * Check to see if this mode usage is equals with another mode usage.
064       */
065      public boolean equals(Object obj) {
066        if (!(obj instanceof ModeUsage))
067          return false;
068        ModeUsage other = (ModeUsage)obj;
069        return this.mode == other.mode && this.currentMode == other.currentMode && Equal.equal(this.modeMap, other.modeMap);
070      }
071    
072      /**
073       * Gets a hash code for this mode usage.
074       */
075      public int hashCode() {
076        int hc = mode.hashCode() ^ currentMode.hashCode();
077        if (modeMap != null)
078          hc ^= modeMap.hashCode();
079        return hc;
080      }
081    
082      /**
083       * Resolves the Mode.CURRENT to the currentMode for this mode usage.
084       * If not Mode.CURRENT passed as argument then the same mode is returned.
085       * @param mode The mode to be resolved.
086       * @return Either the current mode mode usage or the same mode passed as argument.
087       */
088      private Mode resolve(Mode mode) {
089        if (mode == Mode.CURRENT) { 
090          return currentMode;
091        }
092        if (mode.isAnonymous() && !mode.isDefined()) {
093          return currentMode;
094        }
095        return mode;
096      }
097    
098      /**
099       * Get the maximum attribute processing value from the default mode and
100       * from all the modes specified in the contexts.
101       * @return The attribute processing value.
102       */
103      int getAttributeProcessing() {
104        if (attributeProcessing == -1) {
105          attributeProcessing = resolve(mode).getAttributeProcessing();
106          if (modeMap != null) {
107            for (Enumeration en = modeMap.values();
108                 en.hasMoreElements()
109                 && attributeProcessing != Mode.ATTRIBUTE_PROCESSING_FULL;)
110              attributeProcessing = Math.max(resolve((Mode)en.nextElement()).getAttributeProcessing(),
111                                             attributeProcessing);
112          }
113        }
114        return attributeProcessing;
115      }
116    
117      /**
118       * Check if we have context dependent modes.
119       * @return true if the modeMap exists.
120       */
121      boolean isContextDependent() {
122        return modeMap != null;
123      }
124    
125      /**
126       * Get the mode to be used for a specific context.
127       * @param context The current context.
128       * @return A mode.
129       */
130      Mode getMode(Vector context) {
131        // first look in the modeMap if exists.
132        if (modeMap != null) {
133          Mode m = (Mode)modeMap.get(context);
134          if (m != null)
135            return resolve(m);
136        }
137        // if no modeMap or no context specific mode found then
138        // return the default mode for this mode usage.
139        return resolve(mode);
140      }
141    
142      /**
143       * Adds a new context (isRoot, path --> mode).
144       * @param isRoot Flag indicating that the path starts or not with /
145       * @param names The local names that form the path.
146       * @param mode The mode for this path.
147       * @return true if we do not have a duplicate path.
148       */
149      boolean addContext(boolean isRoot, Vector names, Mode mode) {
150        if (modeMap == null)
151          modeMap = new ContextMap();
152        return modeMap.put(isRoot, names, mode);
153      }
154    }