com.amazon.ion.system.SimpleCatalog Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ion-java Show documentation
Show all versions of ion-java Show documentation
A Java implementation of the Amazon Ion data notation.
/*
* Copyright 2007-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.amazon.ion.system;
import com.amazon.ion.IonCatalog;
import com.amazon.ion.IonMutableCatalog;
import com.amazon.ion.SymbolTable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
/**
* A basic implementation of {@link IonCatalog} as a hash table. There is no
* automatic removal of entries.
*/
public class SimpleCatalog
implements IonMutableCatalog, Iterable
{
/* CAVEATS AND LIMITATIONS
*
* - When getTable can't find an exact match, it does a linear scan of
* all tables with the same name to find the best match.
* - Synchonization could probably be tighter using read/write locks
* instead of simple monitors.
*/
private Map> myTablesByName =
new HashMap>();
public SymbolTable getTable(String name)
{
if (name == null)
{
throw new IllegalArgumentException("name is null");
}
if (name.length() == 0)
{
throw new IllegalArgumentException("name is empty");
}
TreeMap versions;
synchronized (myTablesByName)
{
versions = myTablesByName.get(name);
}
if (versions == null) return null;
synchronized (versions)
{
Integer highestVersion = versions.lastKey();
return versions.get(highestVersion);
}
}
public SymbolTable getTable(String name, int version)
{
if (name == null)
{
throw new IllegalArgumentException("name is null");
}
if (name.length() == 0)
{
throw new IllegalArgumentException("name is empty");
}
if (version < 1)
{
throw new IllegalArgumentException("version is < 1");
}
TreeMap versions;
synchronized (myTablesByName)
{
versions = myTablesByName.get(name);
}
if (versions == null) return null;
synchronized (versions)
{
SymbolTable st = versions.get(version);
if (st == null)
{
// if we don't have the one you want, we'll give you the
// "best" one we have, even if it's newer than what you
// asked for (see CAVEAT above)
assert !versions.isEmpty();
Integer ibest = bestMatch(version, versions.keySet());
assert(ibest != null);
st = versions.get(ibest);
assert st != null;
}
return st;
}
}
static Integer bestMatch(int requestedVersion,
Iterable availableVersions)
{
int best = requestedVersion;
Integer ibest = null;
for (Integer available : availableVersions)
{
assert available != requestedVersion;
int v = available.intValue();
if (requestedVersion < best) {
if (requestedVersion < v && v < best) {
best = v;
ibest = available;
}
}
else if (best < requestedVersion) {
if (best < v) {
best = v;
ibest = available;
}
}
else {
best = v;
ibest = available;
}
}
return ibest;
}
public void putTable(SymbolTable table)
{
if (table.isLocalTable() || table.isSystemTable() || table.isSubstitute())
{
throw new IllegalArgumentException("table cannot be local or system or substitute table");
}
String name = table.getName();
int version = table.getVersion();
assert version >= 0;
synchronized (myTablesByName)
{
TreeMap versions =
myTablesByName.get(name);
if (versions == null)
{
versions = new TreeMap();
myTablesByName.put(name, versions);
}
synchronized (versions)
{
versions.put(version, table);
}
}
}
/**
* Removes a symbol table from this catalog.
*
* @return the removed table, or null
if this catalog has
* no matching table.
*/
public SymbolTable removeTable(String name, int version)
{
SymbolTable removed = null;
synchronized (myTablesByName)
{
TreeMap versions =
myTablesByName.get(name);
if (versions != null)
{
synchronized (versions)
{
removed = versions.remove(version);
// Remove empty intermediate table
if (versions.isEmpty())
{
myTablesByName.remove(name);
}
}
}
}
return removed;
}
/**
* Constructs an iterator that enumerates all of the shared symbol tables
* in this catalog, at the time of method invocation. The result represents
* a snapshot of the state of this catalog.
*
* @return a non-null, but potentially empty, iterator.
*/
public Iterator iterator()
{
ArrayList tables;
synchronized (myTablesByName)
{
tables = new ArrayList(myTablesByName.size());
// I don't think we can shorten the synchronization block
// because HashMap.values() result is a live view (not a copy) and
// thus needs to be synched too.
Collection> symtabNames =
myTablesByName.values();
for (TreeMap versions : symtabNames)
{
synchronized (versions)
{
tables.addAll(versions.values());
}
}
}
return tables.iterator();
}
}