260 lines
8.1 KiB
Java
260 lines
8.1 KiB
Java
/*
|
|
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
package sun.util.resources;
|
|
|
|
import java.util.AbstractSet;
|
|
import java.util.Collections;
|
|
import java.util.Enumeration;
|
|
import java.util.HashSet;
|
|
import java.util.Iterator;
|
|
import java.util.NoSuchElementException;
|
|
import java.util.ResourceBundle;
|
|
import java.util.Set;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import java.util.concurrent.ConcurrentMap;
|
|
import java.util.concurrent.atomic.AtomicMarkableReference;
|
|
|
|
/**
|
|
* ParallelListResourceBundle is another variant of ListResourceBundle
|
|
* supporting "parallel" contents provided by another resource bundle
|
|
* (OpenListResourceBundle). Parallel contents, if any, are added into this
|
|
* bundle on demand.
|
|
*
|
|
* @author Masayoshi Okutsu
|
|
*/
|
|
public abstract class ParallelListResourceBundle extends ResourceBundle {
|
|
private volatile ConcurrentMap<String, Object> lookup;
|
|
private volatile Set<String> keyset;
|
|
private final AtomicMarkableReference<Object[][]> parallelContents
|
|
= new AtomicMarkableReference<>(null, false);
|
|
|
|
/**
|
|
* Sole constructor. (For invocation by subclass constructors, typically
|
|
* implicit.)
|
|
*/
|
|
protected ParallelListResourceBundle() {
|
|
}
|
|
|
|
/**
|
|
* Returns an array in which each item is a pair of objects in an
|
|
* Object array. The first element of each pair is the key, which
|
|
* must be a String, and the second element is the value
|
|
* associated with that key. See the class description for
|
|
* details.
|
|
*
|
|
* @return an array of an Object array representing a key-value pair.
|
|
*/
|
|
protected abstract Object[][] getContents();
|
|
|
|
/**
|
|
* Returns the parent of this resource bundle or null if there's no parent.
|
|
*
|
|
* @return the parent or null if no parent
|
|
*/
|
|
ResourceBundle getParent() {
|
|
return parent;
|
|
}
|
|
|
|
/**
|
|
* Sets the parallel contents to the data given by rb. If rb is null, this
|
|
* bundle will be marked as `complete'.
|
|
*
|
|
* @param rb an OpenResourceBundle for parallel contents, or null indicating
|
|
* there are no parallel contents for this bundle
|
|
*/
|
|
public void setParallelContents(OpenListResourceBundle rb) {
|
|
if (rb == null) {
|
|
parallelContents.compareAndSet(null, null, false, true);
|
|
} else {
|
|
parallelContents.compareAndSet(null, rb.getContents(), false, false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns true if any parallel contents have been set or if this bundle is
|
|
* marked as complete.
|
|
*
|
|
* @return true if any parallel contents have been processed
|
|
*/
|
|
boolean areParallelContentsComplete() {
|
|
// Quick check for `complete'
|
|
if (parallelContents.isMarked()) {
|
|
return true;
|
|
}
|
|
boolean[] done = new boolean[1];
|
|
Object[][] data = parallelContents.get(done);
|
|
return data != null || done[0];
|
|
}
|
|
|
|
@Override
|
|
protected Object handleGetObject(String key) {
|
|
if (key == null) {
|
|
throw new NullPointerException();
|
|
}
|
|
|
|
loadLookupTablesIfNecessary();
|
|
return lookup.get(key);
|
|
}
|
|
|
|
@Override
|
|
public Enumeration<String> getKeys() {
|
|
return Collections.enumeration(keySet());
|
|
}
|
|
|
|
@Override
|
|
public boolean containsKey(String key) {
|
|
return keySet().contains(key);
|
|
}
|
|
|
|
@Override
|
|
protected Set<String> handleKeySet() {
|
|
loadLookupTablesIfNecessary();
|
|
return lookup.keySet();
|
|
}
|
|
|
|
@Override
|
|
@SuppressWarnings("UnusedAssignment")
|
|
public Set<String> keySet() {
|
|
Set<String> ks;
|
|
while ((ks = keyset) == null) {
|
|
ks = new KeySet(handleKeySet(), parent);
|
|
synchronized (this) {
|
|
if (keyset == null) {
|
|
keyset = ks;
|
|
}
|
|
}
|
|
}
|
|
return ks;
|
|
}
|
|
|
|
/**
|
|
* Discards any cached keyset value. This method is called from
|
|
* LocaleData for re-creating a KeySet.
|
|
*/
|
|
synchronized void resetKeySet() {
|
|
keyset = null;
|
|
}
|
|
|
|
/**
|
|
* Loads the lookup table if they haven't been loaded already.
|
|
*/
|
|
void loadLookupTablesIfNecessary() {
|
|
ConcurrentMap<String, Object> map = lookup;
|
|
if (map == null) {
|
|
map = new ConcurrentHashMap<>();
|
|
for (Object[] item : getContents()) {
|
|
map.put((String) item[0], item[1]);
|
|
}
|
|
}
|
|
|
|
// If there's any parallel contents data, merge the data into map.
|
|
Object[][] data = parallelContents.getReference();
|
|
if (data != null) {
|
|
for (Object[] item : data) {
|
|
map.putIfAbsent((String) item[0], item[1]);
|
|
}
|
|
parallelContents.set(null, true);
|
|
}
|
|
if (lookup == null) {
|
|
synchronized (this) {
|
|
if (lookup == null) {
|
|
lookup = map;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This class implements the Set interface for
|
|
* ParallelListResourceBundle methods.
|
|
*/
|
|
private static class KeySet extends AbstractSet<String> {
|
|
private final Set<String> set;
|
|
private final ResourceBundle parent;
|
|
|
|
private KeySet(Set<String> set, ResourceBundle parent) {
|
|
this.set = set;
|
|
this.parent = parent;
|
|
}
|
|
|
|
@Override
|
|
public boolean contains(Object o) {
|
|
if (set.contains(o)) {
|
|
return true;
|
|
}
|
|
return (parent != null) ? parent.containsKey((String) o) : false;
|
|
}
|
|
|
|
@Override
|
|
public Iterator<String> iterator() {
|
|
if (parent == null) {
|
|
return set.iterator();
|
|
}
|
|
return new Iterator<String>() {
|
|
private Iterator<String> itr = set.iterator();
|
|
private boolean usingParent;
|
|
|
|
@Override
|
|
public boolean hasNext() {
|
|
if (itr.hasNext()) {
|
|
return true;
|
|
}
|
|
if (!usingParent) {
|
|
Set<String> nextset = new HashSet<>(parent.keySet());
|
|
nextset.removeAll(set);
|
|
itr = nextset.iterator();
|
|
usingParent = true;
|
|
}
|
|
return itr.hasNext();
|
|
}
|
|
|
|
@Override
|
|
public String next() {
|
|
if (hasNext()) {
|
|
return itr.next();
|
|
}
|
|
throw new NoSuchElementException();
|
|
}
|
|
|
|
@Override
|
|
public void remove() {
|
|
throw new UnsupportedOperationException();
|
|
}
|
|
};
|
|
}
|
|
|
|
@Override
|
|
public int size() {
|
|
if (parent == null) {
|
|
return set.size();
|
|
}
|
|
Set<String> allset = new HashSet<>(set);
|
|
allset.addAll(parent.keySet());
|
|
return allset.size();
|
|
}
|
|
}
|
|
}
|