1 /* 2 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.util.resources; 27 28 import java.util.AbstractSet; 29 import java.util.Collections; 30 import java.util.Enumeration; 31 import java.util.HashSet; 32 import java.util.Iterator; 33 import java.util.NoSuchElementException; 34 import java.util.ResourceBundle; 35 import java.util.Set; 36 import java.util.concurrent.ConcurrentHashMap; 37 import java.util.concurrent.ConcurrentMap; 38 import java.util.concurrent.atomic.AtomicMarkableReference; 39 40 /** 41 * ParallelListResourceBundle is another variant of ListResourceBundle 42 * supporting "parallel" contents provided by another resource bundle 43 * (OpenListResourceBundle). Parallel contents, if any, are added into this 44 * bundle on demand. 45 * 46 * @author Masayoshi Okutsu 47 */ 48 public abstract class ParallelListResourceBundle extends ResourceBundle { 49 private volatile ConcurrentMap<String, Object> lookup; 50 private volatile Set<String> keyset; 51 private final AtomicMarkableReference<Object[][]> parallelContents 52 = new AtomicMarkableReference<>(null, false); 53 54 /** 55 * Sole constructor. (For invocation by subclass constructors, typically 56 * implicit.) 57 */ 58 protected ParallelListResourceBundle() { 59 } 60 61 /** 62 * Returns an array in which each item is a pair of objects in an 63 * Object array. The first element of each pair is the key, which 64 * must be a String, and the second element is the value 65 * associated with that key. See the class description for 66 * details. 67 * 68 * @return an array of an Object array representing a key-value pair. 69 */ 70 protected abstract Object[][] getContents(); 71 72 /** 73 * Returns the parent of this resource bundle or null if there's no parent. 74 * 75 * @return the parent or null if no parent 76 */ 77 ResourceBundle getParent() { 78 return parent; 79 } 80 81 /** 82 * Sets the parallel contents to the data given by rb. If rb is null, this 83 * bundle will be marked as `complete'. 84 * 85 * @param rb an OpenResourceBundle for parallel contents, or null indicating 86 * there are no parallel contents for this bundle 87 */ 88 public void setParallelContents(OpenListResourceBundle rb) { 89 if (rb == null) { 90 parallelContents.compareAndSet(null, null, false, true); 91 } else { 92 parallelContents.compareAndSet(null, rb.getContents(), false, false); 93 } 94 } 95 96 /** 97 * Returns true if any parallel contents have been set or if this bundle is 98 * marked as complete. 99 * 100 * @return true if any parallel contents have been processed 101 */ 102 boolean areParallelContentsComplete() { 103 // Quick check for `complete' 104 if (parallelContents.isMarked()) { 105 return true; 106 } 107 boolean[] done = new boolean[1]; 108 Object[][] data = parallelContents.get(done); 109 return data != null || done[0]; 110 } 111 112 @Override 113 protected Object handleGetObject(String key) { 114 if (key == null) { 115 throw new NullPointerException(); 116 } 117 118 loadLookupTablesIfNecessary(); 119 return lookup.get(key); 120 } 121 122 @Override 123 public Enumeration<String> getKeys() { 124 return Collections.enumeration(keySet()); 125 } 126 127 @Override 128 public boolean containsKey(String key) { 129 return keySet().contains(key); 130 } 131 132 @Override 133 protected Set<String> handleKeySet() { 134 loadLookupTablesIfNecessary(); 135 return lookup.keySet(); 136 } 137 138 @Override 139 @SuppressWarnings("UnusedAssignment") 140 public Set<String> keySet() { 141 Set<String> ks; 142 while ((ks = keyset) == null) { 143 ks = new KeySet(handleKeySet(), parent); 144 synchronized (this) { 145 if (keyset == null) { 146 keyset = ks; 147 } 148 } 149 } 150 return ks; 151 } 152 153 /** 154 * Discards any cached keyset value. This method is called from 155 * LocaleData for re-creating a KeySet. 156 */ 157 synchronized void resetKeySet() { 158 keyset = null; 159 } 160 161 /** 162 * Loads the lookup table if they haven't been loaded already. 163 */ 164 void loadLookupTablesIfNecessary() { 165 ConcurrentMap<String, Object> map = lookup; 166 if (map == null) { 167 map = new ConcurrentHashMap<>(); 168 for (Object[] item : getContents()) { 169 map.put((String) item[0], item[1]); 170 } 171 } 172 173 // If there's any parallel contents data, merge the data into map. 174 Object[][] data = parallelContents.getReference(); 175 if (data != null) { 176 for (Object[] item : data) { 177 map.putIfAbsent((String) item[0], item[1]); 178 } 179 parallelContents.set(null, true); 180 } 181 if (lookup == null) { 182 synchronized (this) { 183 if (lookup == null) { 184 lookup = map; 185 } 186 } 187 } 188 } 189 190 /** 191 * This class implements the Set interface for 192 * ParallelListResourceBundle methods. 193 */ 194 private static class KeySet extends AbstractSet<String> { 195 private final Set<String> set; 196 private final ResourceBundle parent; 197 198 private KeySet(Set<String> set, ResourceBundle parent) { 199 this.set = set; 200 this.parent = parent; 201 } 202 203 @Override 204 public boolean contains(Object o) { 205 if (set.contains(o)) { 206 return true; 207 } 208 return (parent != null) ? parent.containsKey((String) o) : false; 209 } 210 211 @Override 212 public Iterator<String> iterator() { 213 if (parent == null) { 214 return set.iterator(); 215 } 216 return new Iterator<>() { 217 private Iterator<String> itr = set.iterator(); 218 private boolean usingParent; 219 220 @Override 221 public boolean hasNext() { 222 if (itr.hasNext()) { 223 return true; 224 } 225 if (!usingParent) { 226 Set<String> nextset = new HashSet<>(parent.keySet()); 227 nextset.removeAll(set); 228 itr = nextset.iterator(); 229 usingParent = true; 230 } 231 return itr.hasNext(); 232 } 233 234 @Override 235 public String next() { 236 if (hasNext()) { 237 return itr.next(); 238 } 239 throw new NoSuchElementException(); 240 } 241 242 @Override 243 public void remove() { 244 throw new UnsupportedOperationException(); 245 } 246 }; 247 } 248 249 @Override 250 public int size() { 251 if (parent == null) { 252 return set.size(); 253 } 254 Set<String> allset = new HashSet<>(set); 255 allset.addAll(parent.keySet()); 256 return allset.size(); 257 } 258 } 259 }