/* * $Id$ * This file is a part of the Arakhne Foundation Classes, http://www.arakhne.org/afc * * Copyright (c) 2000-2012 Stephane GALLAND. * Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports, * Universite de Technologie de Belfort-Montbeliard. * Copyright (c) 2013-2016 The original authors, and other authors. * * 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.arakhne.afc.math.graph; import java.lang.ref.WeakReference; import java.util.Collection; import java.util.Iterator; import java.util.Set; import java.util.TreeSet; import org.eclipse.xtext.xbase.lib.Pure; import org.arakhne.afc.references.ComparableWeakReference; import org.arakhne.afc.references.WeakArrayList; /** * A subgraph. * * <p>The segments is the subgraph are weak references to the segments * to the complete graph. * * @param <PT> is the type of node in the graph * @param <ST> is the type of edge in the graph * @param <GP> is the type of graph path * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @since 13.0 */ public class SubGraph<ST extends GraphSegment<ST, PT>, PT extends GraphPoint<PT, ST>, GP extends GraphPath<GP, ST, PT>> implements Graph<ST, PT> { /** Comparator of graph iteration element on oriented segments. */ protected final GraphIterationElementComparator<ST, PT> iterationOrientedElementComparator; /** Comparator of graph iteration element on not-oriented segments. */ protected final GraphIterationElementComparator<ST, PT> iterationNotOrientedElementComparator; private final Set<ComparableWeakReference<PT>> terminalPoints = new TreeSet<>(); private final Collection<ST> segments; private int pointNumber; private WeakReference<Graph<ST, PT>> parentGraph; /** * @param segments1 is the collection to use to store the segments. * @param pointNumber1 is the number of connection points in the subgraph described by the {@code segments}. * @param orientedIterator is a comparator which may be used by this subgraph. * @param notOrientedIterator is a comparator which may be used by this subgraph. */ protected SubGraph( Collection<ST> segments1, int pointNumber1, GraphIterationElementComparator<ST, PT> orientedIterator, GraphIterationElementComparator<ST, PT> notOrientedIterator) { this.segments = segments1; this.pointNumber = pointNumber1; assert orientedIterator != null; this.iterationOrientedElementComparator = orientedIterator; assert notOrientedIterator != null; this.iterationNotOrientedElementComparator = notOrientedIterator; } /** * Build a subgraph in which graph segments are stored inside * a {@link WeakArrayList} instance. * * @param orientedIterator is a comparator which may be used by this subgraph. * @param notOrientedIterator is a comparator which may be used by this subgraph. */ public SubGraph( GraphIterationElementComparator<ST, PT> orientedIterator, GraphIterationElementComparator<ST, PT> notOrientedIterator) { this( new WeakArrayList<ST>(), 0, orientedIterator, notOrientedIterator); } //----------------------------------------------------------- // SubGraph interface //----------------------------------------------------------- /** Replies the parent graph is this subgraph was built. * * @return the parent graph or <code>null</code> */ @Pure protected final Graph<ST, PT> getParentGraph() { return this.parentGraph == null ? null : this.parentGraph.get(); } /** Replies the segments in this subgraph. * * <p>Caution: the replied collection is the real reference * to the internal collection. Any change in this collection * outside this {@link SubGraph} class may causes invalid * behaviour (no event...). * * @return the internal data structure that contains all the * segments in this subgraph. */ @Pure protected final Collection<ST> getGraphSegments() { return this.segments; } /** Build a subgraph from the specified graph. * * @param iterator is the iterator on the graph. */ public final void build(GraphIterator<ST, PT> iterator) { build(iterator, null); } /** Build a subgraph from the specified graph. * * @param iterator is the iterator on the graph. * @param listener is the listener invoked each time a segment was added to the subgraph. */ public final void build(GraphIterator<ST, PT> iterator, SubGraphBuildListener<ST, PT> listener) { assert iterator != null; final Set<ComparableWeakReference<PT>> reachedPoints = new TreeSet<>(); GraphIterationElement<ST, PT> element; ST segment; PT point; PT firstPoint = null; this.parentGraph = new WeakReference<>(iterator.getGraph()); this.segments.clear(); this.pointNumber = 0; this.terminalPoints.clear(); while (iterator.hasNext()) { element = iterator.nextElement(); point = element.getPoint(); segment = element.getSegment(); // First reached segment if (this.segments.isEmpty()) { firstPoint = point; } this.segments.add(segment); if (listener != null) { listener.segmentAdded(this, element); } // Register terminal points point = segment.getOtherSidePoint(point); final ComparableWeakReference<PT> ref = new ComparableWeakReference<>(point); if (element.isTerminalSegment()) { if (!reachedPoints.contains(ref)) { this.terminalPoints.add(ref); if (listener != null) { listener.terminalPointReached(this, point, segment); } } } else { this.terminalPoints.remove(ref); reachedPoints.add(ref); if (listener != null) { listener.nonTerminalPointReached(this, point, segment); } } } if (firstPoint != null) { final ComparableWeakReference<PT> ref = new ComparableWeakReference<>(firstPoint); if (!reachedPoints.contains(ref)) { this.terminalPoints.add(ref); } } this.pointNumber = this.terminalPoints.size() + reachedPoints.size(); reachedPoints.clear(); } /** Replies if the given point is a terminal point. * * @param point the point to test. * @return <code>true</code> if the point is terminal otherwise <code>false</code> */ @Pure protected final boolean isTerminalPoint(PT point) { return this.terminalPoints.contains(new ComparableWeakReference<>(point)); } /** Create a wrapping segment for the given graph segment. * * @param segment the segment to wrap. * @return a wrapping segment or the {@code segment} itself. */ @Pure protected ST wrapSegment(ST segment) { return segment; } /** Create a wrapping point for the given graph point. * * @param point the point to wrap. * @param segment is the segment connected to the point. * @param isTerminal indicates if this point should be a terminal point. * @return a wrapping point or the {@code point} itself. */ @Pure protected PT wrapPoint(PT point, ST segment, boolean isTerminal) { return point; } private ST filterSegment(ST segment) { if (this.segments.contains(segment)) { return segment; } return null; } //----------------------------------------------------------- // Graph interface //----------------------------------------------------------- @Override @Pure public boolean isEmpty() { return this.segments.isEmpty(); } @Pure @Override public boolean contains(Object obj) { return this.segments.contains(obj); } @Pure @Override public final int getSegmentCount() { return this.segments.size(); } @Pure @Override public final int getPointCount() { return this.pointNumber; } @Pure @Override public GraphIterator<ST, PT> depthIterator( ST startingSegment, double depth, double positionFromStartingPoint, PT startingPoint, boolean allowManyReplies, boolean assumeOrientedSegments) { return new DepthSubGraphIterator( filterSegment(startingSegment), depth, positionFromStartingPoint, startingPoint, allowManyReplies, assumeOrientedSegments); } @Pure @Override public final Iterator<ST> iterator() { return new SubGraphSegmentIterator(this.segments.iterator()); } @Pure @Override public GraphIterator<ST, PT> iterator(ST startingSegment, PT startingPoint, boolean allowManyReplies, boolean assumeOrientedSegments) { return new SubGraphIterator( filterSegment(startingSegment), startingPoint, allowManyReplies, assumeOrientedSegments); } /** Iterator on subgraph segments, with exploration depth. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @since 13.0 */ private class DepthSubGraphIterator extends DepthGraphIterator<ST, PT> { /** Construct the iterator. * * @param startingSegment the starting segment. * @param depth the exploration depth. * @param positionFromStartingPoint the position from the starting point. * @param startingPoint the starting point, attached to the starting segment. * @param allowManyReplies indicates if a segment could be reply many times. * @param assumedOrientedSegments indicates if the segments are assumed to be oriented, * i.e. that are from their starting point to their ending points. */ DepthSubGraphIterator(ST startingSegment, double depth, double positionFromStartingPoint, PT startingPoint, boolean allowManyReplies, boolean assumedOrientedSegments) { super(SubGraph.this, depth, positionFromStartingPoint, startingSegment, startingPoint, allowManyReplies, assumedOrientedSegments); } @Override protected GraphIterationElement<ST, PT> newIterationElement( ST previousSegment, ST segment, PT point, double distanceToReach, double distanceToConsume) { return new SubGraphIterationElement( previousSegment, segment, point, distanceToReach, distanceToConsume); } } /** Iterator on segments of a subgraph, without exploration depth. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @since 13.0 */ private class SubGraphIterator extends GraphIterator<ST, PT> { /** Construct the iterator. * * @param startingSegment the starting segment. * @param startingPoint the starting point, attached to the starting segment. * @param allowCycles indicates if cycles in iteration are allowed. * @param assumeOrientedSegments indicates if the segments are assumed to be oriented, * i.e. that are from their starting point to their ending points. */ SubGraphIterator(ST startingSegment, PT startingPoint, boolean allowCycles, boolean assumeOrientedSegments) { super(SubGraph.this, startingSegment, startingPoint, allowCycles, assumeOrientedSegments, 0.f); } /** Construct the iterator. * * @param startingSegment the starting segment. * @param startingPoint the starting point, attached to the starting segment. * @param assumeOrientedSegments indicates if the segments are assumed to be oriented, * i.e. that are from their starting point to their ending points. */ SubGraphIterator(ST startingSegment, PT startingPoint, boolean assumeOrientedSegments) { super(SubGraph.this, startingSegment, startingPoint, false, assumeOrientedSegments, 0.f); } @Override protected GraphIterationElementComparator<ST, PT> createVisitedSegmentComparator(boolean assumeOrientedSegments) { if (assumeOrientedSegments) { if (SubGraph.this.iterationOrientedElementComparator != null) { return SubGraph.this.iterationOrientedElementComparator; } } else { if (SubGraph.this.iterationNotOrientedElementComparator != null) { return SubGraph.this.iterationNotOrientedElementComparator; } } return super.createVisitedSegmentComparator(assumeOrientedSegments); } @Override protected GraphIterationElement<ST, PT> newIterationElement( ST previousSegment, ST segment, PT point, double distanceToReach, double distanceToConsume) { return new SubGraphIterationElement( previousSegment, segment, point, distanceToReach, distanceToConsume); } } /** Element replied during the iration on a graph. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @since 13.0 */ private final class SubGraphIterationElement extends GraphIterationElement<ST, PT> { private ST wrappedPreviousSegment; private ST wrappedSegment; private PT wrappedPoint; /** * @param previousSegment is the previous element that permits to reach this object during an iteration * @param segment is the current segment * @param point is the point on which the iteration arrived on the current segment. * @param distanceToReach1 is the distance that is already consumed. * @param distanceToConsume1 is the distance to consume including the segment. */ SubGraphIterationElement(ST previousSegment, ST segment, PT point, double distanceToReach1, double distanceToConsume1) { super(previousSegment, segment, point, distanceToReach1, distanceToConsume1); } @Override public ST getPreviousSegment() { if (this.wrappedPreviousSegment == null) { this.wrappedPreviousSegment = wrapSegment(this.previousSegment); } return this.wrappedPreviousSegment; } @Override public ST getSegment() { if (this.wrappedSegment == null) { this.wrappedSegment = wrapSegment(this.currentSegment); } return this.wrappedSegment; } @Override public PT getPoint() { if (this.wrappedPoint == null) { boolean isTerminal = false; ST sgmt = this.previousSegment; if (sgmt == null) { sgmt = this.currentSegment; isTerminal = true; } assert this.connectionPoint != null; assert sgmt != null; this.wrappedPoint = wrapPoint( this.connectionPoint, sgmt, isTerminal || this.lastReachableSegment); } return this.wrappedPoint; } } /** Iterator on segments of a subgraph. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @since 13.0 */ private class SubGraphSegmentIterator implements Iterator<ST> { private final Iterator<ST> iterator; SubGraphSegmentIterator(Iterator<ST> iter) { this.iterator = iter; } @Override public final boolean hasNext() { return this.iterator.hasNext(); } @Override public final ST next() { return wrapSegment(this.iterator.next()); } @Override public final void remove() { // } } }