All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.amazon.ion.system.SimpleCatalog Maven / Gradle / Ivy

The newest version!
/*
 * 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();
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy