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

Source Code of com.orientechnologies.orient.core.db.record.ORecordLazyList

/*
* Copyright 1999-2010 Luca Garulli (l.garulli--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.
*/
package com.orientechnologies.orient.core.db.record;

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

import com.orientechnologies.orient.core.config.OGlobalConfiguration;
import com.orientechnologies.orient.core.db.record.ORecordMultiValueHelper.MULTIVALUE_CONTENT_TYPE;
import com.orientechnologies.orient.core.exception.ORecordNotFoundException;
import com.orientechnologies.orient.core.id.ORID;
import com.orientechnologies.orient.core.id.ORecordId;
import com.orientechnologies.orient.core.record.ORecord;
import com.orientechnologies.orient.core.record.ORecordInternal;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.serialization.serializer.OStringSerializerHelper;

/**
* Lazy implementation of ArrayList. It's bound to a source ORecord object to keep track of changes. This avoid to call the
* makeDirty() by hand when the list is changed. It handles an internal contentType to speed up some operations like conversion
* to/from record/links.
*
* @author Luca Garulli (l.garulli--at--orientechnologies.com)
*
*/
@SuppressWarnings({ "serial" })
public class ORecordLazyList extends ORecordTrackedList implements ORecordLazyMultiValue {
  protected ORecordLazyListener                              listener;
  protected final byte                                      recordType;
  protected ODatabaseRecord                                  database;
  protected ORecordMultiValueHelper.MULTIVALUE_CONTENT_TYPE  contentType          = MULTIVALUE_CONTENT_TYPE.EMPTY;
  protected StringBuilder                                    stream;
  protected boolean                                          autoConvertToRecord  = true;
  protected boolean                                          marshalling          = false;
  protected boolean                                          ridOnly              = false;

  public ORecordLazyList(final ODatabaseRecord iDatabase) {
    super(null);
    this.database = iDatabase;
    this.recordType = ODocument.RECORD_TYPE;
  }

  public ORecordLazyList(final ORecordInternal<?> iSourceRecord) {
    super(iSourceRecord);
    this.database = iSourceRecord.getDatabase();
    this.recordType = iSourceRecord.getRecordType();
  }

  @SuppressWarnings("unchecked")
  @Override
  public boolean addAll(Collection<? extends OIdentifiable> c) {
    final Iterator<OIdentifiable> it = (Iterator<OIdentifiable>) (c instanceof ORecordLazyMultiValue ? ((ORecordLazyMultiValue) c)
        .rawIterator() : c.iterator());

    while (it.hasNext())
      add(it.next());

    return true;
  }

  @Override
  public boolean isEmpty() {
    if (stream == null)
      return super.isEmpty();
    else
      // AVOID TO LAZY LOAD IT, JUST CHECK IF STREAM IS EMPTY OR NULL
      return stream.length() == 0;
  }

  /**
   * Returns a iterator that just returns the elements without convertion.
   *
   * @return
   */
  public Iterator<OIdentifiable> rawIterator() {
    lazyLoad(false);
    final Iterator<OIdentifiable> subIterator = new Iterator<OIdentifiable>() {
      private int  pos  = 0;

      public boolean hasNext() {
        return pos < size();
      }

      public OIdentifiable next() {
        return ORecordLazyList.this.rawGet(pos++);
      }

      public void remove() {
        ORecordLazyList.this.remove(pos);
      }
    };
    return new OLazyRecordIterator(sourceRecord, database, recordType, subIterator, false);
  }

  public OIdentifiable rawGet(final int index) {
    lazyLoad(false);
    return super.get(index);
  }

  @Override
  public Iterator<OIdentifiable> iterator() {
    lazyLoad(false);
    return new OLazyRecordIterator(sourceRecord, database, recordType, super.iterator(), autoConvertToRecord);
  }

  @Override
  public boolean contains(final Object o) {
    if (OGlobalConfiguration.LAZYSET_WORK_ON_STREAM.getValueAsBoolean() && getStreamedContent() != null)
      return getStreamedContent().indexOf(((OIdentifiable) o).getIdentity().toString()) > -1;

    lazyLoad(false);
    return super.contains(o);
  }

  @Override
  public boolean add(OIdentifiable e) {
    if ((ridOnly || contentType == MULTIVALUE_CONTENT_TYPE.ALL_RIDS || OGlobalConfiguration.LAZYSET_WORK_ON_STREAM
        .getValueAsBoolean()) && !e.getIdentity().isNew()&& (e instanceof ODocument && !((ODocument) e).isDirty()))
      // IT'S BETTER TO LEAVE ALL RIDS AND EXTRACT ONLY THIS ONE
      e = e.getIdentity();
    else
      contentType = ORecordMultiValueHelper.updateContentType(contentType, e);

    lazyLoad(true);
    return super.add(e);
  }

  @Override
  public void add(int index, OIdentifiable e) {
    if ((ridOnly || contentType == MULTIVALUE_CONTENT_TYPE.ALL_RIDS || OGlobalConfiguration.LAZYSET_WORK_ON_STREAM
        .getValueAsBoolean()) && !e.getIdentity().isNew() && (e instanceof ODocument && !((ODocument) e).isDirty()))
      // IT'S BETTER TO LEAVE ALL RIDS AND EXTRACT ONLY THIS ONE
      e = e.getIdentity();
    else
      contentType = ORecordMultiValueHelper.updateContentType(contentType, e);

    lazyLoad(true);
    super.add(index, e);
  }

  @Override
  public OIdentifiable set(int index, OIdentifiable e) {
    lazyLoad(true);

    if ((ridOnly || contentType == MULTIVALUE_CONTENT_TYPE.ALL_RIDS || OGlobalConfiguration.LAZYSET_WORK_ON_STREAM
        .getValueAsBoolean()) && !e.getIdentity().isNew()&& (e instanceof ODocument && !((ODocument) e).isDirty()))
      // IT'S BETTER TO LEAVE ALL RIDS AND EXTRACT ONLY THIS ONE
      e = e.getIdentity();
    else
      contentType = ORecordMultiValueHelper.updateContentType(contentType, e);

    return super.set(index, e);
  }

  @Override
  public OIdentifiable get(final int index) {
    lazyLoad(false);
    if (autoConvertToRecord)
      convertLink2Record(index);
    return super.get(index);
  }

  @Override
  public int indexOf(final Object o) {
    lazyLoad(false);
    return super.indexOf(o);
  }

  @Override
  public int lastIndexOf(final Object o) {
    lazyLoad(false);
    return super.lastIndexOf(o);
  }

  @Override
  public OIdentifiable remove(final int iIndex) {
    lazyLoad(true);
    return super.remove(iIndex);
  }

  @Override
  public boolean remove(final Object iElement) {
    final boolean result;
    if (OGlobalConfiguration.LAZYSET_WORK_ON_STREAM.getValueAsBoolean() && getStreamedContent() != null) {
      // WORK ON STREAM
      final StringBuilder stream = getStreamedContent();
      final String rid = ((OIdentifiable) iElement).getIdentity().toString();
      int pos = stream.indexOf(rid);
      if (pos > -1) {
        setDirty();
        // FOUND: REMOVE IT DIRECTLY FROM STREAM
        if (pos > 0)
          pos--;
        stream.delete(pos, pos + rid.length() + 1);
        if (stream.length() == 0)
          setStreamedContent(null);
        result = true;
      } else
        result = false;
    } else {
      lazyLoad(true);
      result = super.remove(iElement);
    }

    if (isEmpty())
      contentType = MULTIVALUE_CONTENT_TYPE.EMPTY;

    return result;
  }

  @Override
  public void clear() {
    super.clear();
    contentType = MULTIVALUE_CONTENT_TYPE.EMPTY;
    stream = null;
  }

  @Override
  public int size() {
    lazyLoad(false);
    return super.size();
  }

  @SuppressWarnings("unchecked")
  @Override
  public <RET> RET setDirty() {
    if (!marshalling)
      return (RET) super.setDirty();
    return (RET) this;
  }

  @Override
  public Object[] toArray() {
    convertLinks2Records();
    return super.toArray();
  }

  @Override
  public <T> T[] toArray(final T[] a) {
    lazyLoad(false);
    convertLinks2Records();
    return super.toArray(a);
  }

  public void convertLinks2Records() {
    lazyLoad(false);
    if (contentType == MULTIVALUE_CONTENT_TYPE.ALL_RECORDS || !autoConvertToRecord || database == null)
      // PRECONDITIONS
      return;

    for (int i = 0; i < size(); ++i) {
      try {
        convertLink2Record(i);
      } catch (ORecordNotFoundException e) {
        // LEAVE THE RID DIRTY
      }
    }

    contentType = MULTIVALUE_CONTENT_TYPE.ALL_RECORDS;
  }

  public boolean convertRecords2Links() {
    if (contentType == MULTIVALUE_CONTENT_TYPE.ALL_RIDS || sourceRecord == null || database == null)
      // PRECONDITIONS
      return true;

    boolean allConverted = true;
    for (int i = 0; i < super.size(); ++i) {
      try {
        if (!convertRecord2Link(i))
          allConverted = false;
      } catch (ORecordNotFoundException e) {
        // LEAVE THE RID DIRTY
      }
    }

    if (allConverted)
      contentType = MULTIVALUE_CONTENT_TYPE.ALL_RIDS;

    return allConverted;
  }

  /**
   * Convert the item requested from link to record.
   *
   * @param iIndex
   *          Position of the item to convert
   */
  private void convertLink2Record(final int iIndex) {
    if (ridOnly || !autoConvertToRecord || database == null)
      // PRECONDITIONS
      return;

    final OIdentifiable o = super.get(iIndex);

    if (contentType == MULTIVALUE_CONTENT_TYPE.ALL_RECORDS && !o.getIdentity().isNew())
      // ALL RECORDS AND THE OBJECT IS NOT NEW, DO NOTHING
      return;

    if (o != null && o instanceof ORecordId) {
      final ORecordId rid = (ORecordId) o;

      marshalling = true;
      try {
        final ORecordInternal<?> record = database.load(rid);

        super.set(iIndex, (OIdentifiable) record);

      } catch (ORecordNotFoundException e) {
        // IGNORE THIS
      } finally {
        marshalling = false;
      }
    }
  }

  /**
   * Convert the item requested from record to link.
   *
   * @param iIndex
   *          Position of the item to convert
   */
  private boolean convertRecord2Link(final int iIndex) {
    if (contentType == MULTIVALUE_CONTENT_TYPE.ALL_RIDS || database == null)
      // PRECONDITIONS
      return true;

    final Object o = super.get(iIndex);

    if (o != null) {
      if (o instanceof ORecord<?> && !((ORecord<?>) o).isDirty()) {
        marshalling = true;
        try {
          super.set(iIndex, ((ORecord<?>) o).getIdentity());
          // CONVERTED
          return true;
        } catch (ORecordNotFoundException e) {
          // IGNORE THIS
        } finally {
          marshalling = false;
        }
      } else if (o instanceof ORID)
        // ALREADY CONVERTED
        return true;
    }
    return false;
  }

  public boolean isAutoConvertToRecord() {
    return autoConvertToRecord;
  }

  public void setAutoConvertToRecord(boolean convertToDocument) {
    this.autoConvertToRecord = convertToDocument;
  }

  @Override
  public String toString() {
    if (stream == null)
      return ORecordMultiValueHelper.toString(this);
    else {
      return "[NOT LOADED: " + stream + ']';
    }
  }

  public byte getRecordType() {
    return recordType;
  }

  @Override
  public boolean setDatabase(final ODatabaseRecord iDatabase) {
    if (database != iDatabase) {
      database = iDatabase;
      super.setDatabase(iDatabase);
      return true;
    }
    return false;
  }

  public ORecordLazyList copy(final ODocument iSourceRecord) {
    final ORecordLazyList copy = new ORecordLazyList(iSourceRecord);
    copy.database = database;
    copy.contentType = contentType;
    copy.stream = stream;
    copy.autoConvertToRecord = autoConvertToRecord;

    final int tot = super.size();
    for (int i = 0; i < tot; ++i)
      copy.rawAdd(rawGet(i));

    return copy;
  }

  public Iterator<OIdentifiable> newItemsIterator() {
    return null;
  }

  public ORecordLazyList setStreamedContent(final StringBuilder iStream) {
    if (iStream == null || iStream.length() == 0)
      stream = null;
    else {
      // CREATE A COPY TO FREE ORIGINAL BUFFER
      stream = iStream;
      final int prevModCount = modCount;
      reset();
      modCount = prevModCount;
    }

    contentType = MULTIVALUE_CONTENT_TYPE.ALL_RIDS;
    return this;
  }

  public StringBuilder getStreamedContent() {
    return stream;
  }

  public ORecordLazyListener getListener() {
    return listener;
  }

  public ORecordLazyList setListener(final ORecordLazyListener listener) {
    this.listener = listener;
    return this;
  }

  public boolean lazyLoad(final boolean iInvalidateStream) {
    if (stream == null)
      return false;

    marshalling = true;
    int currentModCount = modCount;
    final List<String> items = OStringSerializerHelper.smartSplit(stream.toString(), OStringSerializerHelper.RECORD_SEPARATOR);

    for (String item : items) {
      if (item.length() == 0)
        continue;

      super.rawAdd(new ORecordId(item));
    }

    modCount = currentModCount;
    marshalling = false;

    // if (iInvalidateStream)
    stream = null;
    contentType = MULTIVALUE_CONTENT_TYPE.ALL_RIDS;

    if (listener != null)
      listener.onLazyLoad();

    return true;
  }

  public boolean isRidOnly() {
    return ridOnly;
  }

  public ORecordLazyList setRidOnly(boolean ridOnly) {
    this.ridOnly = ridOnly;
    return this;
  }
}
TOP

Related Classes of com.orientechnologies.orient.core.db.record.ORecordLazyList

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.