/* * * * 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 com.orientechnologies.orient.core.command.OCommand; import com.orientechnologies.orient.core.command.OCommandExecutorAbstract; import com.orientechnologies.orient.core.command.OCommandPredicate; import com.orientechnologies.orient.core.db.record.OIdentifiable; import com.orientechnologies.orient.core.exception.OCommandExecutionException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; /** * Base class for traversing. * * @author Luca Garulli */ public class OTraverse implements OCommand, Iterable<OIdentifiable>, Iterator<OIdentifiable> { private OCommandPredicate predicate; private Iterator<? extends OIdentifiable> target; private List<Object> fields = new ArrayList<Object>(); private long resultCount = 0; private long limit = 0; private OIdentifiable lastTraversed; private STRATEGY strategy = STRATEGY.DEPTH_FIRST; private OTraverseContext context = new OTraverseContext(); private int maxDepth = -1; public enum STRATEGY { DEPTH_FIRST, BREADTH_FIRST } /* * Executes a traverse collecting all the result in the returning List<OIdentifiable>. 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<OIdentifiable> execute() { final List<OIdentifiable> result = new ArrayList<OIdentifiable>(); 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 (!OCommandExecutorAbstract.checkInterruption(context)) 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<OIdentifiable> iterator() { return this; } public OTraverseContext getContext() { return context; } public OTraverse target(final Iterable<? extends OIdentifiable> iTarget) { return target(iTarget.iterator()); } public OTraverse target(final OIdentifiable... iRecords) { final List<OIdentifiable> list = new ArrayList<OIdentifiable>(); Collections.addAll(list, iRecords); return target(list.iterator()); } @SuppressWarnings("unchecked") public OTraverse target(final Iterator<? extends OIdentifiable> iTarget) { target = iTarget; context.reset(); new OTraverseRecordSetProcess(this, (Iterator<OIdentifiable>) target, OTraversePath.empty()); return this; } public Iterator<? extends OIdentifiable> 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<Object> 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<Object> 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); } public int getMaxDepth() { return maxDepth; } public void setMaxDepth(final int maxDepth) { this.maxDepth = maxDepth; } }