All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.orientechnologies.orient.core.db.record.OTrackedMap Maven / Gradle / Ivy

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

import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;

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.record.impl.ODocumentInternal;

/**
 * Implementation of LinkedHashMap bound to a source ORecord object to keep track of changes. This avoid to call the makeDirty() by
 * hand when the map is changed.
 * 
 * @author Luca Garulli (l.garulli--at--orientechnologies.com)
 * 
 */
@SuppressWarnings("serial")
public class OTrackedMap extends LinkedHashMap implements ORecordElement, OTrackedMultiValue, Serializable {
  final protected ORecord                           sourceRecord;
  private STATUS                                    status          = STATUS.NOT_LOADED;
  private Set> changeListeners = Collections
                                                                        .newSetFromMap(new WeakHashMap, Boolean>());
  protected Class                                genericClass;
  private final boolean                             embeddedCollection;

  public OTrackedMap(final ORecord iRecord, final Map iOrigin, final Class cls) {
    this(iRecord);
    genericClass = cls;
    if (iOrigin != null && !iOrigin.isEmpty())
      putAll(iOrigin);
  }

  public OTrackedMap(final ORecord iSourceRecord) {
    this.sourceRecord = iSourceRecord;
    embeddedCollection = this.getClass().equals(OTrackedMap.class);
  }

  @Override
  public ORecordElement getOwner() {
    return sourceRecord;
  }

  @Override
  public T put(final Object key, final T value) {
    boolean containsKey = containsKey(key);

    T oldValue = super.put(key, value);

    if (containsKey && oldValue == value)
      return oldValue;

    if (oldValue instanceof ODocument)
      ODocumentInternal.removeOwner((ODocument) oldValue, this);

    addOwnerToEmbeddedDoc(value);

    if (containsKey)
      fireCollectionChangedEvent(new OMultiValueChangeEvent(OMultiValueChangeEvent.OChangeType.UPDATE, key, value,
          oldValue));
    else
      fireCollectionChangedEvent(new OMultiValueChangeEvent(OMultiValueChangeEvent.OChangeType.ADD, key, value));

    return oldValue;
  }

  private void addOwnerToEmbeddedDoc(T e) {
    if (embeddedCollection && e instanceof ODocument && !((ODocument) e).getIdentity().isValid())
      ODocumentInternal.addOwner((ODocument) e, this);
  }

  @Override
  public T remove(final Object iKey) {
    boolean containsKey = containsKey(iKey);
    final T oldValue = super.remove(iKey);

    if (oldValue instanceof ODocument)
      ODocumentInternal.removeOwner((ODocument) oldValue, this);

    if (containsKey)
      fireCollectionChangedEvent(new OMultiValueChangeEvent(OMultiValueChangeEvent.OChangeType.REMOVE, iKey, null,
          oldValue));

    return oldValue;
  }

  @Override
  public void clear() {
    final Map origValues;
    if (changeListeners.isEmpty())
      origValues = null;
    else
      origValues = new HashMap(this);

    if (origValues == null) {
      for (T value : values())
        if (value instanceof ODocument) {
          ODocumentInternal.removeOwner((ODocument) value, this);
        }
    }

    super.clear();

    if (origValues != null) {
      for (Map.Entry entry : origValues.entrySet()) {
        if (entry.getValue() instanceof ODocument) {
          ODocumentInternal.removeOwner((ODocument) entry.getValue(), this);
        }
        fireCollectionChangedEvent(new OMultiValueChangeEvent(OMultiValueChangeEvent.OChangeType.REMOVE, entry.getKey(),
            null, entry.getValue()));
      }
    } else
      setDirty();
  }

  @Override
  public void putAll(Map m) {
    for (Map.Entry entry : m.entrySet()) {
      put(entry.getKey(), entry.getValue());
    }
  }

  @SuppressWarnings({ "unchecked" })
  public OTrackedMap setDirty() {
    if (status != STATUS.UNMARSHALLING && sourceRecord != null
        && !(sourceRecord.isDirty() && ORecordInternal.isContentChanged(sourceRecord)))
      sourceRecord.setDirty();
    return this;
  }

  @Override
  public void setDirtyNoChanged() {
    if (status != STATUS.UNMARSHALLING && sourceRecord != null)
      sourceRecord.setDirtyNoChanged();
  }

  public STATUS getInternalStatus() {
    return status;
  }

  public void setInternalStatus(final STATUS iStatus) {
    status = iStatus;
  }

  public void addChangeListener(OMultiValueChangeListener changeListener) {
    changeListeners.add(changeListener);
  }

  public void removeRecordChangeListener(OMultiValueChangeListener changeListener) {
    changeListeners.remove(changeListener);
  }

  public Map returnOriginalState(final List> multiValueChangeEvents) {
    final Map reverted = new HashMap(this);

    final ListIterator> listIterator = multiValueChangeEvents.listIterator(multiValueChangeEvents
        .size());

    while (listIterator.hasPrevious()) {
      final OMultiValueChangeEvent event = listIterator.previous();
      switch (event.getChangeType()) {
      case ADD:
        reverted.remove(event.getKey());
        break;
      case REMOVE:
        reverted.put(event.getKey(), event.getOldValue());
        break;
      case UPDATE:
        reverted.put(event.getKey(), event.getOldValue());
        break;
      default:
        throw new IllegalArgumentException("Invalid change type : " + event.getChangeType());
      }
    }

    return reverted;
  }

  protected void fireCollectionChangedEvent(final OMultiValueChangeEvent event) {
    if (status == STATUS.UNMARSHALLING)
      return;

    setDirty();
    for (final OMultiValueChangeListener changeListener : changeListeners) {
      if (changeListener != null)
        changeListener.onAfterRecordChanged(event);
    }
  }

  public Class getGenericClass() {
    return genericClass;
  }

  public void setGenericClass(Class genericClass) {
    this.genericClass = genericClass;
  }

  private Object writeReplace() {
    return new LinkedHashMap(this);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy