package jetbrains.mps.ide.depanalyzer; /*Generated by MPS */ import org.apache.log4j.Logger; import org.apache.log4j.LogManager; import org.jetbrains.mps.util.Condition; import java.util.List; import java.util.ArrayList; import java.util.Set; import java.util.HashSet; import jetbrains.mps.internal.collections.runtime.ListSequence; import jetbrains.mps.internal.collections.runtime.IWhereFilter; import java.util.Arrays; /*package*/ class CycleBuilder { private static final Logger LOG = LogManager.getLogger(CycleBuilder.class); private final Condition<DepLink> elementMatch; private DepLink myTarget; private final List<DepPath> myCycles = new ArrayList<DepPath>(); private DepPath myCurrent; private final Set<DepLink> myReusedChecked = new HashSet<DepLink>(); private int myNestLevelDebug; /*package*/ CycleBuilder(Condition<DepLink> elementMatch) { this.elementMatch = elementMatch; } /** * Each cycle found when traversing supplied depLink. * Cycle starts and ends at the same module with the same role, and these are of supplied depLink * elementMatch condition is met for each element of the path (including first and last). */ public List<DepPath> cyclePaths(DepLink depLink) { if (LOG.isDebugEnabled()) { LOG.debug(String.format("\nStart path cycle calculation from %s", depLink)); } myTarget = depLink; myCycles.clear(); myReusedChecked.clear(); myCurrent = new DepPath(); myCurrent.push(depLink); myNestLevelDebug = 0; nextPathLevel(depLink); myCurrent = null; myTarget = null; return myCycles; } private void nextPathLevel(DepLink l) { if (ListSequence.fromList(l.children()).isEmpty() && l.getReused() != null) { if (myReusedChecked.add(l.getReused())) { debug(l, "(reused)"); // reused is identical to the referencing node, don't check key equality to avoid false cycles nextPathLevel(l.getReused()); } else { debug(l, "(reused, already checked, ignored)"); } return; } debug(l, ""); myNestLevelDebug++; for (DepLink ch : ListSequence.fromList(l.children()).where(new IWhereFilter<DepLink>() { public boolean accept(DepLink it) { return elementMatch.met(it); } })) { if (myCurrent.seen(ch)) { if (eq_tn82ka_a0a0a0d0l(ch.getRoleModuleKey(), myTarget.getRoleModuleKey())) { // cycle found myCurrent.push(ch); myCycles.add(new DepPath(myCurrent)); myCurrent.pop(); } continue; } myCurrent.push(ch); nextPathLevel(ch); myCurrent.pop(); } myNestLevelDebug--; } private void debug(DepLink l, String extra) { if (LOG.isDebugEnabled()) { LOG.debug(String.format("%s%s %s %s %s", debugIndent(), l.linktype, l.module.getModuleName(), l.role, extra)); } } private CharSequence debugIndent() { char[] rv = new char[myNestLevelDebug]; Arrays.fill(rv, 0, myNestLevelDebug, ' '); return new String(rv); } private static boolean eq_tn82ka_a0a0a0d0l(Object a, Object b) { return (a != null ? a.equals(b) : a == b); } }