1 /*
   2  * reserved comment block
   3  * DO NOT REMOVE OR ALTER!
   4  */
   5 /*
   6  * Copyright 2002,2004 The Apache Software Foundation.
   7  *
   8  * Licensed under the Apache License, Version 2.0 (the "License");
   9  * you may not use this file except in compliance with the License.
  10  * You may obtain a copy of the License at
  11  *
  12  *      http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 
  21 package com.sun.org.apache.xerces.internal.impl.xs.util;
  22 
  23 import java.util.AbstractMap;
  24 import java.util.AbstractSet;
  25 import java.util.Iterator;
  26 import java.util.Map;
  27 import java.util.NoSuchElementException;
  28 import java.util.Set;
  29 
  30 import javax.xml.XMLConstants;
  31 import javax.xml.namespace.QName;
  32 
  33 import com.sun.org.apache.xerces.internal.util.SymbolHash;
  34 import com.sun.org.apache.xerces.internal.xs.XSNamedMap;
  35 import com.sun.org.apache.xerces.internal.xs.XSObject;
  36 
  37 /**
  38  * Containts the map between qnames and XSObject's.
  39  *
  40  * @xerces.internal
  41  *
  42  * @author Sandy Gao, IBM
  43  *
  44  */
  45 public class XSNamedMapImpl extends AbstractMap implements XSNamedMap {
  46 
  47     /**
  48      * An immutable empty map.
  49      */
  50     public static final XSNamedMapImpl EMPTY_MAP = new XSNamedMapImpl(new XSObject[0], 0);
  51 
  52     // components of these namespaces are stored in this map
  53     final String[] fNamespaces;
  54     // number of namespaces
  55     final int fNSNum;
  56     // each entry contains components in one namespace
  57     final SymbolHash[] fMaps;
  58     // store all components from all namespace.
  59     // used when this map is accessed as a list.
  60     XSObject[] fArray = null;
  61     // store the number of components.
  62     // used when this map is accessed as a list.
  63     int fLength = -1;
  64     // Set of Map.Entry<QName,XSObject> for the java.util.Map methods
  65     private Set fEntrySet = null;
  66 
  67     /**
  68      * Construct an XSNamedMap implementation for one namespace
  69      *
  70      * @param namespace the namespace to which the components belong
  71      * @param map       the map from local names to components
  72      */
  73     public XSNamedMapImpl(String namespace, SymbolHash map) {
  74         fNamespaces = new String[] {namespace};
  75         fMaps = new SymbolHash[] {map};
  76         fNSNum = 1;
  77     }
  78 
  79     /**
  80      * Construct an XSNamedMap implementation for a list of namespaces
  81      *
  82      * @param namespaces the namespaces to which the components belong
  83      * @param maps       the maps from local names to components
  84      * @param num        the number of namespaces
  85      */
  86     public XSNamedMapImpl(String[] namespaces, SymbolHash[] maps, int num) {
  87         fNamespaces = namespaces;
  88         fMaps = maps;
  89         fNSNum = num;
  90     }
  91 
  92     /**
  93      * Construct an XSNamedMap implementation one namespace from an array
  94      *
  95      * @param array     containing all components
  96      * @param length    number of components
  97      */
  98     public XSNamedMapImpl(XSObject[] array, int length) {
  99         if (length == 0) {
 100             fNamespaces = null;
 101             fMaps = null;
 102             fNSNum = 0;
 103             fArray = array;
 104             fLength = 0;
 105             return;
 106         }
 107         // because all components are from the same target namesapce,
 108         // get the namespace from the first one.
 109         fNamespaces = new String[]{array[0].getNamespace()};
 110         fMaps = null;
 111         fNSNum = 1;
 112         // copy elements to the Vector
 113         fArray = array;
 114         fLength = length;
 115     }
 116 
 117     /**
 118      * The number of <code>XSObjects</code> in the <code>XSObjectList</code>.
 119      * The range of valid child object indices is 0 to <code>length-1</code>
 120      * inclusive.
 121      */
 122     public synchronized int getLength() {
 123         if (fLength == -1) {
 124             fLength = 0;
 125             for (int i = 0; i < fNSNum; i++) {
 126                 fLength += fMaps[i].getLength();
 127             }
 128         }
 129         return fLength;
 130     }
 131 
 132     /**
 133      * Retrieves an <code>XSObject</code> specified by local name and
 134      * namespace URI.
 135      * <br>Per XML Namespaces, applications must use the value <code>null</code> as the
 136      * <code>namespace</code> parameter for methods if they wish to specify
 137      * no namespace.
 138      * @param namespace The namespace URI of the <code>XSObject</code> to
 139      *   retrieve, or <code>null</code> if the <code>XSObject</code> has no
 140      *   namespace.
 141      * @param localName The local name of the <code>XSObject</code> to
 142      *   retrieve.
 143      * @return A <code>XSObject</code> (of any type) with the specified local
 144      *   name and namespace URI, or <code>null</code> if they do not
 145      *   identify any object in this map.
 146      */
 147     public XSObject itemByName(String namespace, String localName) {
 148         for (int i = 0; i < fNSNum; i++) {
 149             if (isEqual(namespace, fNamespaces[i])) {
 150                 // when this map is created from SymbolHash's
 151                 // get the component from SymbolHash
 152                 if (fMaps != null) {
 153                     return (XSObject)fMaps[i].get(localName);
 154                 }
 155                 // Otherwise (it's created from an array)
 156                 // go through the array to find a matching name
 157                 XSObject ret;
 158                 for (int j = 0; j < fLength; j++) {
 159                     ret = fArray[j];
 160                     if (ret.getName().equals(localName)) {
 161                         return ret;
 162                     }
 163                 }
 164                 return null;
 165             }
 166         }
 167         return null;
 168     }
 169 
 170     /**
 171      * Returns the <code>index</code>th item in the collection or
 172      * <code>null</code> if <code>index</code> is greater than or equal to
 173      * the number of objects in the list. The index starts at 0.
 174      * @param index  index into the collection.
 175      * @return  The <code>XSObject</code> at the <code>index</code>th
 176      *   position in the <code>XSObjectList</code>, or <code>null</code> if
 177      *   the index specified is not valid.
 178      */
 179     public synchronized XSObject item(int index) {
 180         if (fArray == null) {
 181             // calculate the total number of elements
 182             getLength();
 183             fArray = new XSObject[fLength];
 184             int pos = 0;
 185             // get components from all SymbolHashes
 186             for (int i = 0; i < fNSNum; i++) {
 187                 pos += fMaps[i].getValues(fArray, pos);
 188             }
 189         }
 190         if (index < 0 || index >= fLength) {
 191             return null;
 192         }
 193         return fArray[index];
 194     }
 195 
 196     static boolean isEqual(String one, String two) {
 197         return (one != null) ? one.equals(two) : (two == null);
 198     }
 199 
 200     /*
 201      * java.util.Map methods
 202      */
 203 
 204     public boolean containsKey(Object key) {
 205         return (get(key) != null);
 206     }
 207 
 208     public Object get(Object key) {
 209         if (key instanceof QName) {
 210             final QName name = (QName) key;
 211             String namespaceURI = name.getNamespaceURI();
 212             if (XMLConstants.NULL_NS_URI.equals(namespaceURI)) {
 213                 namespaceURI = null;
 214             }
 215             String localPart = name.getLocalPart();
 216             return itemByName(namespaceURI, localPart);
 217         }
 218         return null;
 219     }
 220 
 221     public int size() {
 222         return getLength();
 223     }
 224 
 225     public synchronized Set entrySet() {
 226         // Defer creation of the entry set until it is actually needed.
 227         if (fEntrySet == null) {
 228             final int length = getLength();
 229             final XSNamedMapEntry[] entries = new XSNamedMapEntry[length];
 230             for (int i = 0; i < length; ++i) {
 231                 XSObject xso = item(i);
 232                 entries[i] = new XSNamedMapEntry(new QName(xso.getNamespace(), xso.getName()), xso);
 233             }
 234             // Create a view of this immutable map.
 235             fEntrySet = new AbstractSet() {
 236                 public Iterator iterator() {
 237                     return new Iterator() {
 238                         private int index = 0;
 239                         public boolean hasNext() {
 240                             return (index < length);
 241                         }
 242                         public Object next() {
 243                             if (index < length) {
 244                                 return entries[index++];
 245                             }
 246                             throw new NoSuchElementException();
 247                         }
 248                         public void remove() {
 249                             throw new UnsupportedOperationException();
 250                         }
 251                     };
 252                 }
 253                 public int size() {
 254                     return length;
 255                 }
 256             };
 257         }
 258         return fEntrySet;
 259     }
 260 
 261     /** An entry in the XSNamedMap. **/
 262     private static final class XSNamedMapEntry implements Map.Entry {
 263         private final QName key;
 264         private final XSObject value;
 265         public XSNamedMapEntry(QName key, XSObject value) {
 266             this.key = key;
 267             this.value = value;
 268         }
 269         public Object getKey() {
 270             return key;
 271         }
 272         public Object getValue() {
 273             return value;
 274         }
 275         public Object setValue(Object value) {
 276             throw new UnsupportedOperationException();
 277         }
 278         public boolean equals(Object o) {
 279             if (o instanceof Map.Entry) {
 280                 Map.Entry e = (Map.Entry) o;
 281                 Object otherKey = e.getKey();
 282                 Object otherValue = e.getValue();
 283                 return (key == null ? otherKey == null : key.equals(otherKey)) &&
 284                     (value == null ? otherValue == null : value.equals(otherValue));
 285             }
 286             return false;
 287         }
 288         public int hashCode() {
 289             return (key == null ? 0 : key.hashCode())
 290                 ^ (value == null ? 0 : value.hashCode());
 291         }
 292         public String toString() {
 293             StringBuffer buffer = new StringBuffer();
 294             buffer.append(String.valueOf(key));
 295             buffer.append('=');
 296             buffer.append(String.valueOf(value));
 297             return buffer.toString();
 298         }
 299     }
 300 
 301 } // class XSNamedMapImpl