com.torodb.core.transaction.metainf.WrapperMutableMetaCollection.java Source code

Java tutorial

Introduction

Here is the source code for com.torodb.core.transaction.metainf.WrapperMutableMetaCollection.java

Source

/*
 * ToroDB
 * Copyright  2014 8Kdata Technology (www.8kdata.com)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

package com.torodb.core.transaction.metainf;

import com.google.common.collect.Maps;
import com.torodb.core.TableRef;
import org.jooq.lambda.Seq;
import org.jooq.lambda.tuple.Tuple2;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Stream;

/**
 *
 */
public class WrapperMutableMetaCollection implements MutableMetaCollection {

    private final ImmutableMetaCollection wrapped;
    private final HashMap<TableRef, WrapperMutableMetaDocPart> newDocParts;
    private final Set<WrapperMutableMetaDocPart> modifiedMetaDocParts;
    private final HashMap<String, Tuple2<MutableMetaIndex, MetaElementState>> indexesByName;
    private final Consumer<WrapperMutableMetaCollection> changeConsumer;
    private final Map<String, Tuple2<MutableMetaIndex, MetaElementState>> aliveIndexesMap;

    public WrapperMutableMetaCollection(ImmutableMetaCollection wrappedCollection,
            Consumer<WrapperMutableMetaCollection> changeConsumer) {
        this.wrapped = wrappedCollection;
        this.changeConsumer = changeConsumer;

        this.newDocParts = new HashMap<>();

        modifiedMetaDocParts = new HashSet<>();

        wrappedCollection.streamContainedMetaDocParts().forEach((docPart) -> {
            WrapperMutableMetaDocPart mutable = createMetaDocPart(docPart);
            newDocParts.put(mutable.getTableRef(), mutable);
        });

        this.indexesByName = new HashMap<>();

        wrappedCollection.streamContainedMetaIndexes().forEach((index) -> {
            WrapperMutableMetaIndex mutable = createMetaIndex(index);
            indexesByName.put(mutable.getName(), new Tuple2<>(mutable, MetaElementState.NOT_CHANGED));
        });
        aliveIndexesMap = Maps.filterValues(indexesByName, tuple -> tuple.v2().isAlive());
    }

    protected WrapperMutableMetaDocPart createMetaDocPart(ImmutableMetaDocPart immutable) {
        return new WrapperMutableMetaDocPart(immutable, this::onDocPartChange);
    }

    protected WrapperMutableMetaIndex createMetaIndex(ImmutableMetaIndex immutable) {
        return new WrapperMutableMetaIndex(immutable, this::onIndexChange);
    }

    @Override
    public ImmutableMetaCollection getOrigin() {
        return wrapped;
    }

    @Override
    public WrapperMutableMetaDocPart addMetaDocPart(TableRef tableRef, String tableId)
            throws IllegalArgumentException {
        if (getMetaDocPartByTableRef(tableRef) != null) {
            throw new IllegalArgumentException("There is another doc part whose table ref is " + tableRef);
        }

        assert getMetaDocPartByIdentifier(tableId) == null : "There is another doc part whose id is " + tableRef;

        WrapperMutableMetaDocPart result = createMetaDocPart(new ImmutableMetaDocPart(tableRef, tableId));

        newDocParts.put(tableRef, result);
        onDocPartChange(result);

        return result;
    }

    @Override
    public Stream<? extends WrapperMutableMetaDocPart> streamModifiedMetaDocParts() {
        return modifiedMetaDocParts.stream();
    }

    @Override
    public MutableMetaIndex addMetaIndex(String name, boolean unique) throws IllegalArgumentException {
        if (getMetaIndexByName(name) != null) {
            throw new IllegalArgumentException("There is another index whose name is " + name);
        }

        WrapperMutableMetaIndex result = createMetaIndex(new ImmutableMetaIndex(name, unique));

        indexesByName.put(name, new Tuple2<>(result, MetaElementState.ADDED));
        changeConsumer.accept(this);

        return result;
    }

    @Override
    public boolean removeMetaIndexByName(String indexName) {
        WrapperMutableMetaIndex metaIndex = getMetaIndexByName(indexName);
        if (metaIndex == null) {
            return false;
        }

        indexesByName.put(metaIndex.getName(), new Tuple2<>(metaIndex, MetaElementState.REMOVED));
        changeConsumer.accept(this);
        return true;
    }

    @Override
    public Stream<ChangedElement<MutableMetaIndex>> streamModifiedMetaIndexes() {
        return indexesByName.values().stream().filter(tuple -> tuple.v2().hasChanged())
                .map(PojoChangedElement::new);
    }

    @Override
    public ImmutableMetaCollection immutableCopy() {
        if (modifiedMetaDocParts.isEmpty()
                && indexesByName.values().stream().noneMatch(tuple -> tuple.v2().hasChanged())) {
            return wrapped;
        } else {
            ImmutableMetaCollection.Builder builder = new ImmutableMetaCollection.Builder(wrapped);
            for (MutableMetaDocPart modifiedMetaDocPart : modifiedMetaDocParts) {
                builder.put(modifiedMetaDocPart.immutableCopy());
            }

            indexesByName.values().forEach(tuple -> {
                switch (tuple.v2()) {
                case ADDED:
                case MODIFIED:
                case NOT_CHANGED:
                    builder.put(tuple.v1().immutableCopy());
                    break;
                case REMOVED:
                    builder.remove(tuple.v1());
                    break;
                case NOT_EXISTENT:
                default:
                    throw new AssertionError("Unexpected case" + tuple.v2());
                }
            });
            return builder.build();
        }
    }

    @Override
    public String getName() {
        return wrapped.getName();
    }

    @Override
    public String getIdentifier() {
        return wrapped.getIdentifier();
    }

    @Override
    public Stream<? extends WrapperMutableMetaDocPart> streamContainedMetaDocParts() {
        return newDocParts.values().stream();
    }

    @Override
    public WrapperMutableMetaDocPart getMetaDocPartByIdentifier(String docPartId) {
        Optional<WrapperMutableMetaDocPart> newDocPart = newDocParts.values().stream()
                .filter((docPart) -> docPart.getIdentifier().equals(docPartId)).findAny();

        return newDocPart.orElse(null);
    }

    @Override
    public WrapperMutableMetaDocPart getMetaDocPartByTableRef(TableRef tableRef) {
        return newDocParts.get(tableRef);
    }

    @Override
    public Stream<? extends WrapperMutableMetaIndex> streamContainedMetaIndexes() {
        return aliveIndexesMap.values().stream().map(tuple -> (WrapperMutableMetaIndex) tuple.v1());
    }

    @Override
    public WrapperMutableMetaIndex getMetaIndexByName(String indexName) {
        Tuple2<MutableMetaIndex, MetaElementState> tuple = aliveIndexesMap.get(indexName);
        if (tuple == null) {
            return null;
        }
        return (WrapperMutableMetaIndex) tuple.v1();
    }

    @Override
    public List<Tuple2<MetaIndex, List<String>>> getMissingIndexesForNewField(MutableMetaDocPart docPart,
            MetaField newField) {
        return wrapped.getMissingIndexesForNewField(streamContainedMetaIndexes(), docPart, newField);
    }

    @Override
    public Optional<ImmutableMetaDocPart> getAnyDocPartWithMissedDocPartIndex(ImmutableMetaCollection oldStructure,
            MutableMetaIndex newIndex) {
        return newIndex
                .streamTableRefs().map(
                        tableRef -> oldStructure.getMetaDocPartByTableRef(tableRef))
                .filter(docPart -> docPart != null && newIndex.isCompatible(docPart)
                        && Seq.seq(newIndex.iteratorMetaDocPartIndexesIdentifiers(docPart))
                                .filter(identifiers -> docPart.streamIndexes().noneMatch(
                                        docPartIndex -> newIndex.isMatch(docPart, identifiers, docPartIndex)))
                                .anyMatch(identifiers -> {
                                    MutableMetaDocPart newDocPart = getMetaDocPartByTableRef(docPart.getTableRef());
                                    return Seq.seq(newDocPart.streamModifiedMetaDocPartIndexes()).filter(
                                            docPartIndex -> docPartIndex.getChange() != MetaElementState.REMOVED)
                                            .noneMatch(docPartIndex -> newIndex.isMatch(newDocPart, identifiers,
                                                    docPartIndex.getElement()));
                                }))
                .findAny();
    }

    @Override
    public Optional<? extends MetaIdentifiedDocPartIndex> getAnyOrphanDocPartIndex(
            ImmutableMetaCollection oldStructure, MutableMetaIndex newRemovedIndex) {
        return newRemovedIndex.streamTableRefs()
                .map(tableRef -> (MetaDocPart) oldStructure.getMetaDocPartByTableRef(tableRef))
                .filter(docPart -> docPart != null && newRemovedIndex.isCompatible(docPart))
                .flatMap(oldDocPart -> oldDocPart.streamIndexes().filter(oldDocPartIndex -> newRemovedIndex
                        .isCompatible(oldDocPart, oldDocPartIndex)
                        && Seq.seq(getMetaDocPartByTableRef(oldDocPart.getTableRef())
                                .streamModifiedMetaDocPartIndexes()).noneMatch(
                                        newDocPartIndex -> newDocPartIndex.getChange() == MetaElementState.REMOVED
                                                && newDocPartIndex.getElement().getIdentifier()
                                                        .equals(oldDocPartIndex.getIdentifier()))
                        && oldStructure.streamContainedMetaIndexes()
                                .noneMatch(oldIndex -> oldIndex.isCompatible(oldDocPart, oldDocPartIndex) && Seq
                                        .seq(streamModifiedMetaIndexes())
                                        .noneMatch(newIndex -> newIndex.getChange() == MetaElementState.REMOVED
                                                && newIndex.getElement().getName().equals(oldIndex.getName())))))
                .findAny();
    }

    @Override
    public String toString() {
        return defautToString();
    }

    protected void onDocPartChange(WrapperMutableMetaDocPart changedDocPart) {
        modifiedMetaDocParts.add(changedDocPart);
        changeConsumer.accept(this);
    }

    private boolean isTransitionAllowed(MetaIndex metaIndex, MetaElementState newState) {
        MetaElementState oldState;
        Tuple2<MutableMetaIndex, MetaElementState> tuple = indexesByName.get(metaIndex.getName());

        if (tuple == null) {
            oldState = MetaElementState.NOT_EXISTENT;
        } else {
            oldState = tuple.v2();
        }

        oldState.assertLegalTransition(newState);
        return true;
    }

    protected void onIndexChange(WrapperMutableMetaIndex changedIndex) {
        assert isTransitionAllowed(changedIndex, MetaElementState.MODIFIED);

        indexesByName.put(changedIndex.getName(), new Tuple2<>(changedIndex, MetaElementState.MODIFIED));
        changeConsumer.accept(this);
    }

}