Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.finos.legend.pure.m4.tools.GraphNodeIterable Maven / Gradle / Ivy
// Copyright 2020 Goldman Sachs
//
// 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.finos.legend.pure.m4.tools;
import org.eclipse.collections.api.LazyIterable;
import org.eclipse.collections.api.factory.Lists;
import org.eclipse.collections.api.factory.Sets;
import org.eclipse.collections.api.list.ImmutableList;
import org.eclipse.collections.api.list.MutableList;
import org.eclipse.collections.api.set.MutableSet;
import org.eclipse.collections.impl.set.mutable.SynchronizedMutableSet;
import org.eclipse.collections.impl.utility.Iterate;
import org.finos.legend.pure.m4.ModelRepository;
import org.finos.legend.pure.m4.coreinstance.CoreInstance;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;
import java.util.Objects;
import java.util.Spliterator;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* An iterable that iterates through the nodes of a graph, starting from a given set of nodes and traversing to
* connected nodes. The traversal of the graph can be controlled by providing a
* {@link java.util.function.Function function} from nodes to {@link GraphWalkFilterResult GraphWalkFilterResults}.
*/
public class GraphNodeIterable extends AbstractLazySpliterable
{
private final ImmutableList startingNodes;
private final BiPredicate super CoreInstance, ? super String> keyFilter;
private final Function super CoreInstance, ? extends GraphWalkFilterResult> nodeFilter;
private GraphNodeIterable(ImmutableList startingNodes, BiPredicate super CoreInstance, ? super String> keyFilter, Function super CoreInstance, ? extends GraphWalkFilterResult> nodeFilter)
{
this.startingNodes = Lists.immutable.withAll(startingNodes);
this.keyFilter = (keyFilter == null) ? (n, k) -> true : keyFilter;
this.nodeFilter = nodeFilter;
}
@Override
public int size()
{
return computeClosure().size();
}
@Override
public boolean isEmpty()
{
return this.startingNodes.isEmpty() || ((this.nodeFilter != null) && !stream().findAny().isPresent());
}
@Override
public CoreInstance getAny()
{
if (this.startingNodes.isEmpty())
{
return null;
}
if (this.nodeFilter == null)
{
return this.startingNodes.getAny();
}
return super.getAny();
}
@Override
public CoreInstance getFirst()
{
if (this.startingNodes.isEmpty())
{
return null;
}
if (this.nodeFilter == null)
{
return this.startingNodes.getFirst();
}
return super.getFirst();
}
@Override
public boolean contains(Object object)
{
return (object instanceof CoreInstance) && super.contains(object);
}
@Override
public LazyIterable distinct()
{
return this;
}
@Override
public MutableList toList()
{
return Lists.mutable.withAll(computeClosure());
}
@Override
public MutableSet toSet()
{
return computeClosure();
}
@Override
public Object[] toArray()
{
return computeClosure().toArray();
}
@Override
public E[] toArray(E[] array)
{
return computeClosure().toArray(array);
}
@Override
public Spliterator spliterator()
{
return new GraphNodeSpliterator(this.startingNodes, this.keyFilter, this.nodeFilter);
}
private MutableSet computeClosure()
{
return computeClosure(this.startingNodes, this.keyFilter, this.nodeFilter);
}
@Deprecated
public static GraphNodeIterable fromNode(CoreInstance startingNode)
{
return builder().withStartingNode(startingNode).build();
}
@Deprecated
public static GraphNodeIterable fromNode(CoreInstance startingNode, Function super CoreInstance, ? extends GraphWalkFilterResult> filter)
{
return builder().withStartingNode(startingNode).withNodeFilter(filter).build();
}
@Deprecated
public static GraphNodeIterable fromNodes(CoreInstance... startingNodes)
{
return builder().withStartingNodes(startingNodes).build();
}
@Deprecated
public static GraphNodeIterable fromNodes(Iterable extends CoreInstance> startingNodes)
{
return builder().withStartingNodes(startingNodes).build();
}
@Deprecated
public static GraphNodeIterable fromNodes(Iterable extends CoreInstance> startingNodes, Function super CoreInstance, ? extends GraphWalkFilterResult> filter)
{
return builder().withStartingNodes(startingNodes).withNodeFilter(filter).build();
}
public static GraphNodeIterable fromModelRepository(ModelRepository repository)
{
return fromModelRepository(repository, null, null);
}
public static GraphNodeIterable fromModelRepository(ModelRepository repository, Function super CoreInstance, ? extends GraphWalkFilterResult> nodeFilter)
{
return fromModelRepository(repository, null, nodeFilter);
}
public static GraphNodeIterable fromModelRepository(ModelRepository repository, BiPredicate super CoreInstance, ? super String> keyFilter, Function super CoreInstance, ? extends GraphWalkFilterResult> nodeFilter)
{
return builder()
.withStartingNodes(repository.getTopLevels())
.withKeyFilter(keyFilter)
.withNodeFilter(nodeFilter)
.build();
}
public static MutableSet allInstancesFromRepository(ModelRepository repository)
{
return allConnectedInstances(repository.getTopLevels());
}
public static MutableSet allConnectedInstances(Iterable extends CoreInstance> startingNodes)
{
return allConnectedInstances(startingNodes, null, null);
}
public static MutableSet allConnectedInstances(Iterable extends CoreInstance> startingNodes, BiPredicate super CoreInstance, ? super String> keyFilter)
{
return allConnectedInstances(startingNodes, keyFilter, null);
}
public static MutableSet allConnectedInstances(Iterable extends CoreInstance> startingNodes, Function super CoreInstance, ? extends GraphWalkFilterResult> nodeFilter)
{
return allConnectedInstances(startingNodes, null, nodeFilter);
}
public static MutableSet allConnectedInstances(Iterable extends CoreInstance> startingNodes, BiPredicate super CoreInstance, ? super String> keyFilter, Function super CoreInstance, ? extends GraphWalkFilterResult> nodeFilter)
{
return computeClosure(startingNodes, keyFilter, nodeFilter);
}
private static MutableSet computeClosure(Iterable extends CoreInstance> startingNodes, BiPredicate super CoreInstance, ? super String> keyFilter, Function super CoreInstance, ? extends GraphWalkFilterResult> nodeFilter)
{
GraphNodeSpliterator spliterator = new GraphNodeSpliterator(startingNodes, keyFilter, nodeFilter);
spliterator.forEachRemaining(n ->
{
// Do nothing: collect instances by side effect
});
return spliterator.visited;
}
private static class GraphNodeSpliterator implements Spliterator
{
private final Deque deque;
private MutableSet visited;
private final BiPredicate super CoreInstance, ? super String> keyFilter;
private final Function super CoreInstance, ? extends GraphWalkFilterResult> nodeFilter;
private GraphNodeSpliterator(Deque deque, MutableSet visited, BiPredicate super CoreInstance, ? super String> keyFilter, Function super CoreInstance, ? extends GraphWalkFilterResult> nodeFilter)
{
this.deque = deque;
this.visited = visited;
this.keyFilter = (keyFilter == null) ? (n, k) -> true : keyFilter;
this.nodeFilter = nodeFilter;
}
private GraphNodeSpliterator(Iterable extends CoreInstance> startingNodes, BiPredicate super CoreInstance, ? super String> keyFilter, Function super CoreInstance, ? extends GraphWalkFilterResult> nodeFilter)
{
this(Iterate.addAllTo(startingNodes, new ArrayDeque<>()), Sets.mutable.empty(), keyFilter, nodeFilter);
}
@Override
public boolean tryAdvance(Consumer super CoreInstance> action)
{
while (!this.deque.isEmpty())
{
CoreInstance node = this.deque.pollFirst();
if (this.visited.add(node))
{
GraphWalkFilterResult filterResult = filterNode(node);
if (filterResult.shouldContinue())
{
node.getKeys().forEach(key ->
{
if (this.keyFilter.test(node, key))
{
node.getValueForMetaPropertyToMany(key).forEach(v ->
{
if (!this.visited.contains(v))
{
this.deque.addLast(v);
}
});
}
});
}
if (filterResult.shouldAccept())
{
action.accept(node);
return true;
}
}
}
return false;
}
@Override
public Spliterator trySplit()
{
if (this.deque.size() < 2)
{
return null;
}
int splitSize = this.deque.size() / 2;
Deque newDeque = new ArrayDeque<>(splitSize);
for (int i = 0; i < splitSize; i++)
{
newDeque.addFirst(this.deque.pollLast());
}
// If we are going to split, we need to make sure the visited set is synchronized
if (!(this.visited instanceof SynchronizedMutableSet))
{
this.visited = SynchronizedMutableSet.of(this.visited, this.visited);
}
return new GraphNodeSpliterator(newDeque, this.visited, this.keyFilter, this.nodeFilter);
}
@Override
public long estimateSize()
{
return this.deque.isEmpty() ? 0L : Long.MAX_VALUE;
}
@Override
public long getExactSizeIfKnown()
{
return this.deque.isEmpty() ? 0L : -1L;
}
@Override
public int characteristics()
{
return NONNULL | DISTINCT;
}
private GraphWalkFilterResult filterNode(CoreInstance node)
{
if (this.nodeFilter != null)
{
GraphWalkFilterResult result = this.nodeFilter.apply(node);
if (result != null)
{
return result;
}
}
return GraphWalkFilterResult.ACCEPT_AND_CONTINUE;
}
}
public static Builder builder()
{
return new Builder();
}
public static class Builder
{
private final MutableList startNodes = Lists.mutable.empty();
private BiPredicate super CoreInstance, ? super String> keyFilter;
private Function super CoreInstance, ? extends GraphWalkFilterResult> nodeFilter;
private Builder()
{
}
public Builder withKeyFilter(BiPredicate super CoreInstance, ? super String> keyFilter)
{
this.keyFilter = keyFilter;
return this;
}
public Builder withNodeFilter(Function super CoreInstance, ? extends GraphWalkFilterResult> nodeFilter)
{
this.nodeFilter = nodeFilter;
return this;
}
public Builder withStartingNode(CoreInstance node)
{
this.startNodes.add(Objects.requireNonNull(node));
return this;
}
public Builder withStartingNodes(Iterable extends CoreInstance> nodes)
{
nodes.forEach(this::withStartingNode);
return this;
}
public Builder withStartingNodes(CoreInstance... startingNodes)
{
return withStartingNodes(Arrays.asList(startingNodes));
}
public GraphNodeIterable build()
{
return new GraphNodeIterable(this.startNodes.toImmutable(), this.keyFilter, this.nodeFilter);
}
}
}