1 /* 2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3 * @LastModified: Oct 2017 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 com.sun.org.apache.xerces.internal.util.SymbolHash; 25 import com.sun.org.apache.xerces.internal.xs.XSNamedMap; 26 import com.sun.org.apache.xerces.internal.xs.XSObject; 27 import java.util.AbstractMap; 28 import java.util.AbstractSet; 29 import java.util.Iterator; 30 import java.util.Map; 31 import java.util.NoSuchElementException; 32 import java.util.Set; 33 import javax.xml.XMLConstants; 34 import javax.xml.namespace.QName; 35 36 /** 37 * Containts the map between qnames and XSObject's. 38 * 39 * @xerces.internal 40 * 41 * @author Sandy Gao, IBM 42 * 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