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