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

com.orientechnologies.orient.core.command.traverse.OTraverse 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.command.traverse;

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

import com.orientechnologies.orient.core.command.OCommand;
import com.orientechnologies.orient.core.command.OCommandPredicate;
import com.orientechnologies.orient.core.db.record.OIdentifiable;
import com.orientechnologies.orient.core.exception.OCommandExecutionException;

/**
 * Base class for traversing.
 * 
 * @author Luca Garulli
 */
public class OTraverse implements OCommand, Iterable, Iterator {
  private OCommandPredicate                 predicate;
  private Iterator target;
  private List                      fields      = new ArrayList();
  private long                              resultCount = 0;
  private long                              limit       = 0;
  private OIdentifiable                     lastTraversed;
  private STRATEGY                          strategy    = STRATEGY.DEPTH_FIRST;
  private OTraverseContext                  context     = new OTraverseContext();

  public enum STRATEGY {
    DEPTH_FIRST, BREADTH_FIRST
  }

  /*
   * Executes a traverse collecting all the result in the returning List. This could be memory expensive because for
   * large results the list could be huge. it's always better to use it as an Iterable and lazy fetch each result on next() call.
   * 
   * @see com.orientechnologies.orient.core.command.OCommand#execute()
   */
  public List execute() {
    final List result = new ArrayList();
    while (hasNext())
      result.add(next());
    return result;
  }

  public OTraverseAbstractProcess nextProcess() {
    return context.next();
  }

  public boolean hasNext() {
    if (limit > 0 && resultCount >= limit)
      return false;

    if (lastTraversed == null)
      // GET THE NEXT
      lastTraversed = next();

    if (lastTraversed == null && !context.isEmpty())
      throw new IllegalStateException("Traverse ended abnormally");

    if (!context.checkTimeout())
      return false;

    // BROWSE ALL THE RECORDS
    return lastTraversed != null;
  }

  public OIdentifiable next() {
    if (Thread.interrupted())
      throw new OCommandExecutionException("The traverse execution has been interrupted");

    if (lastTraversed != null) {
      // RETURN LATEST AND RESET IT
      final OIdentifiable result = lastTraversed;
      lastTraversed = null;
      return result;
    }

    if (limit > 0 && resultCount >= limit)
      return null;

    OIdentifiable result;
    OTraverseAbstractProcess toProcess;
    // RESUME THE LAST PROCESS
    while ((toProcess = nextProcess()) != null) {
      result = toProcess.process();
      if (result != null) {
        resultCount++;
        return result;
      }
    }

    return null;
  }

  public void remove() {
    throw new UnsupportedOperationException("remove()");
  }

  public Iterator iterator() {
    return this;
  }

  public OTraverseContext getContext() {
    return context;
  }

  public OTraverse target(final Iterable iTarget) {
    return target(iTarget.iterator());
  }

  public OTraverse target(final OIdentifiable... iRecords) {
    final List list = new ArrayList();
    Collections.addAll(list, iRecords);
    return target(list.iterator());
  }

  @SuppressWarnings("unchecked")
  public OTraverse target(final Iterator iTarget) {
    target = iTarget;
    context.reset();
    new OTraverseRecordSetProcess(this, (Iterator) target, OTraversePath.empty());
    return this;
  }

  public Iterator getTarget() {
    return target;
  }

  public OTraverse predicate(final OCommandPredicate iPredicate) {
    predicate = iPredicate;
    return this;
  }

  public OCommandPredicate getPredicate() {
    return predicate;
  }

  public OTraverse field(final Object iField) {
    if (!fields.contains(iField))
      fields.add(iField);
    return this;
  }

  public OTraverse fields(final Collection iFields) {
    for (Object f : iFields)
      field(f);
    return this;
  }

  public OTraverse fields(final String... iFields) {
    for (String f : iFields)
      field(f);
    return this;
  }

  public List getFields() {
    return fields;
  }

  public long getLimit() {
    return limit;
  }

  public OTraverse limit(final long iLimit) {
    if (iLimit < -1)
      throw new IllegalArgumentException("Limit cannot be negative. 0 = infinite");
    this.limit = iLimit;
    return this;
  }

  @Override
  public String toString() {
    return String.format("OTraverse.target(%s).fields(%s).limit(%d).predicate(%s)", target, fields, limit, predicate);
  }

  public long getResultCount() {
    return resultCount;
  }

  public OIdentifiable getLastTraversed() {
    return lastTraversed;
  }

  public STRATEGY getStrategy() {
    return strategy;
  }

  public void setStrategy(STRATEGY strategy) {
    this.strategy = strategy;
    context.setStrategy(strategy);
  }
}