Package com.orientechnologies.orient.core.db.record.ridbag

Source Code of com.orientechnologies.orient.core.db.record.ridbag.ORidBag

/*
  *
  *  *  Copyright 2014 Orient Technologies LTD (info(at)orientechnologies.com)
  *  *
  *  *  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.
  *  *
  *  * For more information: http://www.orientechnologies.com
  *
  */

package com.orientechnologies.orient.core.db.record.ridbag;

import com.orientechnologies.common.collection.OCollection;
import com.orientechnologies.common.serialization.types.OByteSerializer;
import com.orientechnologies.common.serialization.types.OUUIDSerializer;
import com.orientechnologies.orient.core.config.OGlobalConfiguration;
import com.orientechnologies.orient.core.db.ODatabaseRecordThreadLocal;
import com.orientechnologies.orient.core.db.record.OIdentifiable;
import com.orientechnologies.orient.core.db.record.OMultiValueChangeEvent;
import com.orientechnologies.orient.core.db.record.OMultiValueChangeListener;
import com.orientechnologies.orient.core.db.record.ORecordLazyMultiValue;
import com.orientechnologies.orient.core.db.record.OTrackedMultiValue;
import com.orientechnologies.orient.core.db.record.ridbag.embedded.OEmbeddedRidBag;
import com.orientechnologies.orient.core.db.record.ridbag.sbtree.OBonsaiCollectionPointer;
import com.orientechnologies.orient.core.db.record.ridbag.sbtree.OSBTreeCollectionManager;
import com.orientechnologies.orient.core.db.record.ridbag.sbtree.OSBTreeRidBag;
import com.orientechnologies.orient.core.exception.OSerializationException;
import com.orientechnologies.orient.core.index.sbtreebonsai.local.OSBTreeBonsai;
import com.orientechnologies.orient.core.record.ORecord;
import com.orientechnologies.orient.core.serialization.OBase64Utils;
import com.orientechnologies.orient.core.serialization.serializer.record.binary.BytesContainer;
import com.orientechnologies.orient.core.serialization.serializer.string.OStringBuilderSerializable;
import com.orientechnologies.orient.core.storage.impl.local.paginated.ORecordSerializationContext;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;

/**
* A collection that contain links to {@link OIdentifiable}. Bag is similar to set but can contain several entering of the same
* object.<br>
*
* Could be tree based and embedded representation.<br>
* <ul>
* <li>
* <b>Embedded</b> stores its content directly to the document that owns it.<br>
* It better fits for cases when only small amount of links are stored to the bag.<br>
* </li>
* <li>
* <b>Tree-based</b> implementation stores its content in a separate data structure called {@link OSBTreeBonsai}.<br>
* It fits great for cases when you have a huge amount of links.<br>
* </li>
* </ul>
* <br>
* The representation is automatically converted to tree-based implementation when top threshold is reached. And backward to
* embedded one when size is decreased to bottom threshold. <br>
* The thresholds could be configured by {@link OGlobalConfiguration#RID_BAG_EMBEDDED_TO_SBTREEBONSAI_THRESHOLD} and
* {@link OGlobalConfiguration#RID_BAG_SBTREEBONSAI_TO_EMBEDDED_THRESHOLD}. <br>
* <br>
* This collection is used to efficiently manage relationships in graph model.<br>
* <br>
* Does not implement {@link Collection} interface because some operations could not be efficiently implemented and that's why
* should be avoided.<br>
*
* @author <a href="mailto:enisher@gmail.com">Artem Orobets</a>
* @author Andrey Lomakin
* @since 1.7rc1
*/
public class ORidBag implements OStringBuilderSerializable, Iterable<OIdentifiable>, ORecordLazyMultiValue,
    OTrackedMultiValue<OIdentifiable, OIdentifiable>, OCollection<OIdentifiable> {
  private ORidBagDelegate delegate;

  private int             topThreshold    = OGlobalConfiguration.RID_BAG_EMBEDDED_TO_SBTREEBONSAI_THRESHOLD.getValueAsInteger();
  private int             bottomThreshold = OGlobalConfiguration.RID_BAG_SBTREEBONSAI_TO_EMBEDDED_THRESHOLD.getValueAsInteger();

  private UUID            uuid;

  public ORidBag(final ORidBag ridBag) {
    init();
    for (OIdentifiable identifiable : ridBag)
      add(identifiable);
  }

  public ORidBag() {
    init();
  }

  public ORidBag(final int iTopThreshold, final int iBottomThreshold) {
    topThreshold = iTopThreshold;
    bottomThreshold = iBottomThreshold;
    init();
  }

  private ORidBag(final byte[] stream) {
    fromStream(stream);
  }

  public static ORidBag fromStream(final String value) {
    final byte[] stream = OBase64Utils.decode(value);
    return new ORidBag(stream);
  }

  public ORidBag copy() {
    final ORidBag copy = new ORidBag();
    copy.topThreshold = topThreshold;
    copy.bottomThreshold = bottomThreshold;
    copy.uuid = uuid;

    if (delegate instanceof OSBTreeRidBag)
      // ALREADY MULTI-THREAD
      copy.delegate = delegate;
    else
      copy.delegate = ((OEmbeddedRidBag) delegate).copy();

    return copy;
  }

  public void addAll(Collection<OIdentifiable> values) {
    delegate.addAll(values);
  }

  public void add(OIdentifiable identifiable) {
    delegate.add(identifiable);
  }

  public void remove(OIdentifiable identifiable) {
    delegate.remove(identifiable);
  }

  public boolean isEmpty() {
    return delegate.isEmpty();
  }

  @Override
  public Iterator<OIdentifiable> iterator() {
    return delegate.iterator();
  }

  @Override
  public Iterator<OIdentifiable> rawIterator() {
    return delegate.rawIterator();
  }

  @Override
  public void convertLinks2Records() {
    delegate.convertLinks2Records();
  }

  @Override
  public boolean convertRecords2Links() {
    return delegate.convertRecords2Links();
  }

  @Override
  public boolean isAutoConvertToRecord() {
    return delegate.isAutoConvertToRecord();
  }

  @Override
  public void setAutoConvertToRecord(boolean convertToRecord) {
    delegate.setAutoConvertToRecord(convertToRecord);
  }

  @Override
  public boolean detach() {
    return delegate.detach();
  }

  @Override
  public int size() {
    return delegate.size();
  }

  public boolean isEmbedded() {
    return delegate instanceof OEmbeddedRidBag;
  }

  public int toStream(BytesContainer bytesContainer) throws OSerializationException {

    final ORecordSerializationContext context = ORecordSerializationContext.getContext();
    if (context != null) {
      if (delegate.size() >= topThreshold && isEmbedded()
          && ODatabaseRecordThreadLocal.INSTANCE.get().getSbTreeCollectionManager() != null) {
        ORidBagDelegate oldDelegate = delegate;
        delegate = new OSBTreeRidBag();
        boolean oldAutoConvert = oldDelegate.isAutoConvertToRecord();
        oldDelegate.setAutoConvertToRecord(false);

        for (OIdentifiable identifiable : oldDelegate)
          delegate.add(identifiable);

        final ORecord owner = oldDelegate.getOwner();
        delegate.setOwner(owner);
        owner.setDirty();

        oldDelegate.setAutoConvertToRecord(oldAutoConvert);
        oldDelegate.requestDelete();

      } else if (delegate.size() <= bottomThreshold && !isEmbedded()) {
        ORidBagDelegate oldDelegate = delegate;
        boolean oldAutoConvert = oldDelegate.isAutoConvertToRecord();
        oldDelegate.setAutoConvertToRecord(false);
        delegate = new OEmbeddedRidBag();

        for (OIdentifiable identifiable : oldDelegate)
          delegate.add(identifiable);

        final ORecord owner = oldDelegate.getOwner();
        delegate.setOwner(owner);
        owner.setDirty();

        oldDelegate.setAutoConvertToRecord(oldAutoConvert);
        oldDelegate.requestDelete();
      }
    }

    final UUID oldUuid = uuid;
    final OSBTreeCollectionManager sbTreeCollectionManager = ODatabaseRecordThreadLocal.INSTANCE.get().getSbTreeCollectionManager();
    if (sbTreeCollectionManager != null)
      uuid = sbTreeCollectionManager.listenForChanges(this);
    else
      uuid = null;

    boolean hasUuid = uuid != null;

    final int serializedSize = OByteSerializer.BYTE_SIZE + delegate.getSerializedSize()
        + ((hasUuid) ? OUUIDSerializer.UUID_SIZE : 0);
    int pointer = bytesContainer.alloc((short) serializedSize);
    int offset = pointer;
    final byte[] stream = bytesContainer.bytes;

    byte configByte = 0;
    if (isEmbedded())
      configByte |= 1;

    if (hasUuid)
      configByte |= 2;

    stream[offset++] = configByte;

    if (hasUuid) {
      OUUIDSerializer.INSTANCE.serialize(uuid, stream, offset);
      offset += OUUIDSerializer.UUID_SIZE;
    }

    delegate.serialize(stream, offset, oldUuid);
    return pointer;
  }

  @Override
  public OStringBuilderSerializable toStream(StringBuilder output) throws OSerializationException {
    final BytesContainer container = new BytesContainer();
    toStream(container);
    output.append(OBase64Utils.encodeBytes(container.bytes, 0, container.offset));
    return this;
  }

  @Override
  public String toString() {
    return delegate.toString();
  }

  public void delete() {
    delegate.requestDelete();
  }

  @Override
  public OStringBuilderSerializable fromStream(StringBuilder input) throws OSerializationException {
    final byte[] stream = OBase64Utils.decode(input.toString());
    fromStream(stream);
    return this;
  }

  public void fromStream(final byte[] stream) {
    fromStream(new BytesContainer(stream));
  }

  public void fromStream(BytesContainer stream) {
    final byte first = stream.bytes[stream.offset++];
    if ((first & 1) == 1)
      delegate = new OEmbeddedRidBag();
    else
      delegate = new OSBTreeRidBag();

    if ((first & 2) == 2) {
      uuid = OUUIDSerializer.INSTANCE.deserialize(stream.bytes, stream.offset);
      stream.skip(OUUIDSerializer.UUID_SIZE);
    }

    stream.skip(delegate.deserialize(stream.bytes, stream.offset));
  }

  @Override
  public void addChangeListener(final OMultiValueChangeListener<OIdentifiable, OIdentifiable> changeListener) {
    delegate.addChangeListener(changeListener);
  }

  @Override
  public void removeRecordChangeListener(final OMultiValueChangeListener<OIdentifiable, OIdentifiable> changeListener) {
    delegate.removeRecordChangeListener(changeListener);
  }

  @Override
  public Object returnOriginalState(List<OMultiValueChangeEvent<OIdentifiable, OIdentifiable>> multiValueChangeEvents) {
    return delegate.returnOriginalState(multiValueChangeEvents);
  }

  @Override
  public Class<?> getGenericClass() {
    return delegate.getGenericClass();
  }

  public void setOwner(ORecord owner) {
    delegate.setOwner(owner);
  }

  /**
   * Temporary id of collection to track changes in remote mode.
   *
   * WARNING! Method is for internal usage.
   *
   * @return UUID
   */
  public UUID getTemporaryId() {
    return uuid;
  }

  /**
   * Notify collection that changes has been saved. Converts to non embedded implementation if needed.
   *
   * WARNING! Method is for internal usage.
   *
   * @param newPointer
   *          new collection pointer
   */
  public void notifySaved(OBonsaiCollectionPointer newPointer) {
    if (newPointer.isValid()) {
      if (isEmbedded()) {
        replaceWithSBTree(newPointer);
      } else {
        ((OSBTreeRidBag) delegate).setCollectionPointer(newPointer);
        ((OSBTreeRidBag) delegate).clearChanges();
      }
    }
  }

  public OBonsaiCollectionPointer getPointer() {
    if (isEmbedded()) {
      return OBonsaiCollectionPointer.INVALID;
    } else {
      return ((OSBTreeRidBag) delegate).getCollectionPointer();
    }
  }

  /**
   * IMPORTANT! Only for internal usage.
   */
  public boolean tryMerge(final ORidBag otherValue, boolean iMergeSingleItemsOfMultiValueFields) {
    if (!isEmbedded() && !otherValue.isEmbedded()) {
      final OSBTreeRidBag thisTree = (OSBTreeRidBag) delegate;
      final OSBTreeRidBag otherTree = (OSBTreeRidBag) otherValue.delegate;
      if (thisTree.getCollectionPointer().equals(otherTree.getCollectionPointer())) {

        thisTree.mergeChanges(otherTree);

        uuid = otherValue.uuid;

        return true;
      }
    } else if (iMergeSingleItemsOfMultiValueFields) {
      final Iterator<OIdentifiable> iter = otherValue.rawIterator();
      while (iter.hasNext()) {
        final OIdentifiable value = iter.next();
        if (value != null) {
          final Iterator<OIdentifiable> localIter = rawIterator();
          boolean found = false;
          while (localIter.hasNext()) {
            final OIdentifiable v = localIter.next();
            if (value.equals(v)) {
              found = true;
              break;
            }
          }
          if (!found)
            add(value);
        }
      }
      return true;
    }
    return false;
  }

  protected void init() {
    if (topThreshold < 0)
      delegate = new OSBTreeRidBag();
    else
      delegate = new OEmbeddedRidBag();
  }

  /**
   * Silently replace delegate by tree implementation.
   *
   * @param pointer
   *          new collection pointer
   */
  private void replaceWithSBTree(OBonsaiCollectionPointer pointer) {
    delegate.requestDelete();
    final OSBTreeRidBag treeBag = new OSBTreeRidBag();
    treeBag.setCollectionPointer(pointer);
    delegate = treeBag;
  }
}
TOP

Related Classes of com.orientechnologies.orient.core.db.record.ridbag.ORidBag

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.