SubstituteSymbolTable.java

/*
 * 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.impl;

import com.amazon.ion.IonReader;
import com.amazon.ion.IonWriter;
import com.amazon.ion.ReadOnlyValueException;
import com.amazon.ion.SymbolTable;
import com.amazon.ion.SymbolToken;
import java.io.IOException;
import java.util.Collections;
import java.util.Iterator;

/**
 * A symbol table used for an import where no exact match could be found.
 * This ensures that the declared max_id and version are exposed through the
 * library.
 * <p>
 * Note that {@link #isSubstitute()} is always true for instances of this
 * class.
 */
final class SubstituteSymbolTable
    implements SymbolTable
{
    /**
     * This is the original symbol table that is being substituted.
     * Either null or a non-system shared table.
     */
    private final SymbolTable myOriginalSymTab;
    private final String myName;
    private final int myVersion;
    private final int myMaxId;


    /**
     * @param name
     * @param version
     * @param maxId
     */
    SubstituteSymbolTable(String name, int version,
                          int maxId)
    {
        myOriginalSymTab = null;
        myName = name;
        myVersion = version;
        myMaxId = maxId;
    }

    SubstituteSymbolTable(SymbolTable original, int version, int maxId)
    {
        assert original.isSharedTable() && ! original.isSystemTable();
        assert (original.getVersion() != version
                || original.getMaxId() != maxId);

        myOriginalSymTab = original;
        myName = original.getName();
        myVersion = version;
        myMaxId = maxId;
    }

    public String getName()
    {
        return myName;
    }

    public int getVersion()
    {
        return myVersion;
    }

    /**
     * {@inheritDoc}
     * <p>
     * This implementation always returns true.
     */
    public boolean isSubstitute()
    {
        return true;
    }

    public boolean isLocalTable()
    {
        return false;
    }

    public boolean isSharedTable()
    {
        return true;
    }

    public boolean isSystemTable()
    {
        return false;
    }

    public boolean isReadOnly()
    {
        return true;
    }

    public void makeReadOnly()
    {
    }

    public SymbolTable getSystemSymbolTable()
    {
        return null;
    }

    public String getIonVersionId()
    {
        return null;
    }

    public SymbolTable[] getImportedTables()
    {
        return null;
    }

    public int getImportedMaxId()
    {
        return 0;
    }

    public int getMaxId()
    {
        return myMaxId;
    }

    public SymbolToken intern(String text)
    {
        SymbolToken tok = find(text);
        if (tok == null)
        {
            throw new ReadOnlyValueException(SymbolTable.class);
        }
        return tok;
    }

    public SymbolToken find(String text)
    {
        SymbolToken tok = null;
        if (myOriginalSymTab != null)
        {
            tok = myOriginalSymTab.find(text);
            // If symbol token is found but its sid is beyond the correct max
            // id of the substitute, then return null, as it should not be
            // found at all.
            if (tok != null && tok.getSid() > myMaxId)
            {
                tok = null;
            }
        }
        return tok;
    }

    public int findSymbol(String text)
    {
        int sid = UNKNOWN_SYMBOL_ID;
        if (myOriginalSymTab != null)
        {
            sid = myOriginalSymTab.findSymbol(text);
            if (sid > myMaxId)
            {
                sid = UNKNOWN_SYMBOL_ID;
            }
        }
        return sid;
    }

    public String findKnownSymbol(int id)
    {
        if (id > myMaxId || myOriginalSymTab == null)
        {
            return null;
        }
        return myOriginalSymTab.findKnownSymbol(id);
    }

    @SuppressWarnings("unchecked")
    public Iterator<String> iterateDeclaredSymbolNames()
    {
        Iterator<String> originalIterator;
        if (myOriginalSymTab != null)
        {
            originalIterator = myOriginalSymTab.iterateDeclaredSymbolNames();
        }
        else
        {
            originalIterator = Collections.EMPTY_LIST.iterator();
        }
        return new SymbolIterator(originalIterator);
    }

    public void writeTo(IonWriter writer) throws IOException
    {
        IonReader reader = new SymbolTableReader(this);
        writer.writeValues(reader);
    }

    private final class SymbolIterator implements Iterator<String>
    {
        private Iterator<String> myOriginalIterator;
        private int myIndex = 0;

        SymbolIterator(Iterator<String> originalIterator)
        {
            myOriginalIterator = originalIterator;
        }

        public boolean hasNext()
        {
            if (myIndex < myMaxId) {
                return true;
            }
            return false;
        }

        public String next()
        {
            // TODO bad failure mode if next() called beyond end
            if (myIndex < myMaxId) {
                String name = null;
                if (myOriginalIterator.hasNext())
                {
                    name = myOriginalIterator.next();
                }
                myIndex++;
                return name;
            }
            return null;
        }

        public void remove()
        {
            throw new UnsupportedOperationException();
        }
    }
}