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