jetbrains.exodus.entitystore.iterate.PropertiesIterableWrapper.java Source code

Java tutorial

Introduction

Here is the source code for jetbrains.exodus.entitystore.iterate.PropertiesIterableWrapper.java

Source

/**
 * Copyright 2010 - 2015 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License 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 jetbrains.exodus.entitystore.iterate;

import jetbrains.exodus.core.dataStructures.persistent.AbstractPersistent23Tree;
import jetbrains.exodus.core.dataStructures.persistent.Persistent23Tree;
import jetbrains.exodus.entitystore.*;
import jetbrains.exodus.entitystore.tables.PropertyTypes;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

@SuppressWarnings({ "RawUseOfParameterizedType", "ComparableImplementedButEqualsNotOverridden", "unchecked" })
public class PropertiesIterableWrapper extends UpdatableCachedWrapperIterable {

    private static final Log log = LogFactory.getLog(PropertiesIterableWrapper.class);

    private int entityTypeId;
    @NotNull
    private final Persistent23Tree<IndexEntry> index;
    @Nullable
    private Persistent23Tree.MutableTree<IndexEntry> mutableIndex;
    @Nullable
    private Class<? extends Comparable> valueClass;

    @SuppressWarnings({ "ConstantConditions", "ObjectAllocationInLoop" })
    public PropertiesIterableWrapper(@Nullable final PersistentEntityStoreImpl store,
            @Nullable final PropertyValueIterator it, @NotNull final EntityIterableBase source) {
        super(store, source);
        index = new Persistent23Tree<>();
        mutableIndex = null;
        if (it == null) {
            entityTypeId = -1;
            return;
        }
        try {
            if (!it.hasNext()) {
                entityTypeId = -1;
                valueClass = null;
            } else {
                final Collection<IndexEntry> tempList = new ArrayList<>();
                EntityId id = it.nextId();
                entityTypeId = id.getTypeId();
                Comparable propValue = it.currentValue();
                valueClass = propValue.getClass();
                while (true) {
                    tempList.add(new IndexEntry(propValue, id.getLocalId()));
                    if (!it.hasNext()) {
                        break;
                    }
                    id = it.nextId();
                    propValue = it.currentValue();
                    if (!valueClass.equals(propValue.getClass())) {
                        throw new EntityStoreException("Unexpected property value class");
                    }
                }
                final Persistent23Tree.MutableTree<IndexEntry> mutableTree = index.beginWrite();
                mutableTree.addAll(tempList, tempList.size());
                mutableTree.endWrite();
            }
        } finally {
            ((EntityIteratorBase) it).disposeIfShouldBe();
        }
    }

    // constructor for mutating source index
    private PropertiesIterableWrapper(@NotNull final PropertiesIterableWrapper source) {
        super(source.getStore(), source);
        entityTypeId = source.entityTypeId;
        index = source.index.getClone();
        mutableIndex = index.beginWrite();
        valueClass = source.valueClass;
    }

    @Nullable
    public Class<? extends Comparable> getPropertyValueClass() {
        return valueClass;
    }

    @Override
    public boolean isSortedById() {
        return false;
    }

    public PropertiesIterableWrapper beginUpdate() {
        return new PropertiesIterableWrapper(this);
    }

    @Override
    public boolean isMutated() {
        return mutableIndex != null;
    }

    public void endUpdate() {
        Persistent23Tree.MutableTree<IndexEntry> index = mutableIndex;
        if (index == null) {
            throw new IllegalStateException("PropertiesIterableWrapper was not mutated");
        }
        index.endWrite();
        mutableIndex = null;
    }

    public void update(final int typeId, final long localId, @Nullable final Comparable oldValue,
            @Nullable final Comparable newValue) {
        if (entityTypeId == -1) {
            entityTypeId = typeId;
        }
        final IndexEntry oldEntry = oldValue == null ? null
                : new IndexEntry(PropertyTypes.toLowerCase(oldValue), localId);
        final IndexEntry newEntry = newValue == null ? null
                : new IndexEntry(PropertyTypes.toLowerCase(newValue), localId);
        if (oldEntry == null && newEntry == null) {
            throw new IllegalStateException("Can't update in-memory index: both oldValue and newValue are null");
        }
        final Persistent23Tree.MutableTree<IndexEntry> index = mutableIndex;
        if (index == null) {
            throw new IllegalStateException("Mutate index before updating it");
        }
        if (oldEntry != null) {
            if (index.contains(oldEntry)) {
                index.exclude(oldEntry);
            } else if (newEntry != null && !index.contains(newEntry)) {
                log.warn("In-memory index doesn't contain the value [" + oldValue + "]. New value [" + newValue
                        + "]. Handle [" + getHandle() + ']');
            }
        }
        if (newEntry != null) {
            index.add(newEntry);
            if (valueClass == null) {
                valueClass = newValue.getClass();
            }
        }
    }

    EntityIteratorBase getPropertyValueIterator(@NotNull final Comparable value) {
        return new PropertyValueIteratorWrapper(value);
    }

    EntityIteratorBase getPropertyRangeIterator(@NotNull final Comparable min, @NotNull final Comparable max) {
        return new PropertyRangeIteratorWrapper(min, max);
    }

    @Override
    @NotNull
    public EntityIteratorBase getIteratorImpl(@NotNull final PersistentStoreTransaction txn) {
        return getCurrentTree().isEmpty() ? EntityIteratorBase.EMPTY : new PropertiesIteratorWrapper();
    }

    @Override
    @NotNull
    public EntityIteratorBase getReverseIteratorImpl(@NotNull final PersistentStoreTransaction txn) {
        return getCurrentTree().isEmpty() ? EntityIteratorBase.EMPTY : new ReversePropertiesIteratorWrapper();
    }

    @Override
    public long size() {
        return getCurrentTree().size();
    }

    @Override
    protected long countImpl(@NotNull final PersistentStoreTransaction txn) {
        return size();
    }

    private AbstractPersistent23Tree<IndexEntry> getCurrentTree() {
        return mutableIndex == null ? index.getCurrent() : mutableIndex;
    }

    /**
     * Index key.
     */

    private static class IndexEntry implements Comparable<IndexEntry> {

        @NotNull
        private final Comparable propValue;
        private final long localId;

        private IndexEntry(@NotNull final Comparable propValue, final long localId) {
            this.propValue = propValue;
            this.localId = localId;
        }

        @Override
        public int compareTo(IndexEntry o) {
            int result = propValue.compareTo(o.propValue);
            if (result == 0) {
                if (localId < o.localId) {
                    result = -1;
                } else if (localId > o.localId) {
                    result = 1;
                }
            }
            return result;
        }
    }

    /**
     * Iterators over index.
     */

    @SuppressWarnings({ "PackageVisibleField", "ProtectedField" })
    private abstract class PropertiesIteratorWrapperBase extends NonDisposableEntityIterator
            implements PropertyValueIterator {

        @NotNull
        private final Iterator<IndexEntry> it;
        @Nullable
        protected IndexEntry next;
        protected boolean hasNextValid;

        protected PropertiesIteratorWrapperBase(@NotNull final Iterator<IndexEntry> it) {
            super(PropertiesIterableWrapper.this);
            this.it = it;
        }

        @Override
        protected boolean hasNextImpl() {
            if (hasNextValid) {
                return true;
            }
            if (!it.hasNext()) {
                return false;
            }
            final IndexEntry next = it.next();
            if (!checkIndexEntry(next)) {
                return false;
            }
            this.next = next;
            hasNextValid = true;
            return true;
        }

        @Override
        @Nullable
        public EntityId nextIdImpl() {
            if (!hasNextImpl()) {
                return null;
            }
            //noinspection ConstantConditions
            EntityId result = new PersistentEntityId(entityTypeId, next.localId);
            hasNextValid = false;
            return result;
        }

        @Override
        @Nullable
        public Comparable currentValue() {
            final IndexEntry entry = next;
            return entry == null ? null : entry.propValue;
        }

        protected boolean checkIndexEntry(final IndexEntry entry) {
            return entry != null;
        }
    }

    private class PropertiesIteratorWrapper extends PropertiesIteratorWrapperBase {

        private PropertiesIteratorWrapper() {
            super(getCurrentTree().iterator());
        }
    }

    private class ReversePropertiesIteratorWrapper extends PropertiesIteratorWrapperBase {

        private ReversePropertiesIteratorWrapper() {
            super(getCurrentTree().reverseIterator());
        }
    }

    private class PropertyValueIteratorWrapper extends PropertiesIteratorWrapperBase {

        @NotNull
        private final Comparable value;

        private PropertyValueIteratorWrapper(@NotNull final Comparable value) {
            super(getCurrentTree().tailIterator(new IndexEntry(value, 0)));
            this.value = value;
        }

        @Override
        protected boolean checkIndexEntry(final IndexEntry entry) {
            return entry != null && entry.propValue.compareTo(value) == 0;
        }
    }

    private class PropertyRangeIteratorWrapper extends PropertiesIteratorWrapperBase {

        @NotNull
        private final Comparable max;

        private PropertyRangeIteratorWrapper(@NotNull final Comparable min, @NotNull final Comparable max) {
            super(getCurrentTree().tailIterator(new IndexEntry(min, 0)));
            this.max = max;
        }

        @Override
        protected boolean checkIndexEntry(final IndexEntry entry) {
            return entry != null && entry.propValue.compareTo(max) <= 0;
        }
    }
}