/* * Copyright 2003-2014 JetBrains s.r.o. * * 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 org.jetbrains.mps.util; import org.jetbrains.annotations.NotNull; import org.jetbrains.mps.openapi.language.SAbstractConcept; import org.jetbrains.mps.openapi.language.SConcept; import org.jetbrains.mps.openapi.language.SInterfaceConcept; import java.util.ArrayDeque; import java.util.Deque; import java.util.Iterator; /** * Traverse hierarchy of {@link org.jetbrains.mps.openapi.language.SConcept SConcepts} for a given concept (inclusive), visiting super-concepts first * then super-interfaces in an order they were specified in super-concepts (breadth-like). FIXME make it truly breadth-first, for interface concepts as well. * Given ConceptA implements I1, I2 and ConceptB extends ConceptA implements I3, I4, interface I3 extends I5, interface I5 extends I1, and ConceptB as starting point, * the order would be ConceptB, ConceptA, I3, I4, I1, I2, I5, I1 * * Note, same concept may appear few times in this iterator, no unique filtering is done. Use {@link org.jetbrains.mps.util.UniqueIterator} if necessary. * * FIXME functionality of this class shall get exposed from SConcept API * (likely, in addition to public iterator not to limit to single iteration approach, i.e. depth or breadth first). * @author Artem Tikhomirov * * XXX How come its name is DepthFirst when it is clearly breadth first (for interfaces at least)? */ public class DepthFirstConceptIterator implements Iterable<SAbstractConcept>, Iterator<SAbstractConcept> { private final SAbstractConcept myStart; private SConcept myCurrent; // super-concepts hierarchy or null once all super-concepts are over private final Deque<SInterfaceConcept> myInterfaces = new ArrayDeque<SInterfaceConcept>(); public DepthFirstConceptIterator(@NotNull SAbstractConcept start) { myStart = start; reset(); // just in case we are instantiated as Iterator, not as Iterable } @Override public boolean hasNext() { return myCurrent != null || !myInterfaces.isEmpty(); } @Override public SAbstractConcept next() { if (myCurrent == null) { final SInterfaceConcept rv = myInterfaces.removeFirst(); queue(rv.getSuperInterfaces()); return rv; } else { SConcept rv = myCurrent; queue(myCurrent.getSuperInterfaces()); myCurrent = myCurrent.getSuperConcept(); return rv; } } @Override public void remove() { throw new UnsupportedOperationException(); } private void queue(Iterable<SInterfaceConcept> superInterfaces) { if (superInterfaces != null) { //myInterfaces.addAll(IterableUtil.asList(superInterfaces)); IterableUtil shall move out from kernel module to some utility location for (SInterfaceConcept ic : superInterfaces) { myInterfaces.add(ic); } } } @Override public Iterator<SAbstractConcept> iterator() { reset(); return this; } private void reset() { myInterfaces.clear(); if (myStart instanceof SInterfaceConcept) { myCurrent = null; myInterfaces.add((SInterfaceConcept) myStart); } else { myCurrent = (SConcept) myStart; } } }