Package cascading.flow.planner.graph

Source Code of cascading.flow.planner.graph.ElementGraphs$EdgeAttributeProvider

/*
* Copyright (c) 2007-2014 Concurrent, Inc. All Rights Reserved.
*
* Project and contact information: http://www.cascading.org/
*
* This file is part of the Cascading project.
*
* 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 cascading.flow.planner.graph;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;

import cascading.flow.FlowElement;
import cascading.flow.planner.BaseFlowStep;
import cascading.flow.planner.PlatformInfo;
import cascading.flow.planner.Scope;
import cascading.flow.planner.iso.expression.ElementCapture;
import cascading.flow.planner.iso.expression.ExpressionGraph;
import cascading.flow.planner.iso.expression.FlowElementExpression;
import cascading.flow.planner.iso.expression.TypeExpression;
import cascading.flow.planner.iso.finder.SearchOrder;
import cascading.flow.planner.iso.subgraph.SubGraphIterator;
import cascading.flow.planner.iso.subgraph.iterator.ExpressionSubGraphIterator;
import cascading.pipe.Group;
import cascading.pipe.HashJoin;
import cascading.pipe.Pipe;
import cascading.pipe.Splice;
import cascading.tap.Tap;
import cascading.util.Pair;
import cascading.util.Util;
import cascading.util.Version;
import org.jgrapht.DirectedGraph;
import org.jgrapht.Graph;
import org.jgrapht.GraphPath;
import org.jgrapht.Graphs;
import org.jgrapht.alg.BiconnectivityInspector;
import org.jgrapht.alg.FloydWarshallShortestPaths;
import org.jgrapht.alg.KShortestPaths;
import org.jgrapht.ext.ComponentAttributeProvider;
import org.jgrapht.ext.EdgeNameProvider;
import org.jgrapht.ext.IntegerNameProvider;
import org.jgrapht.ext.VertexNameProvider;
import org.jgrapht.graph.EdgeReversedGraph;
import org.jgrapht.graph.SimpleDirectedGraph;
import org.jgrapht.graph.SimpleGraph;
import org.jgrapht.traverse.TopologicalOrderIterator;
import org.jgrapht.util.TypeUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static cascading.util.Util.getFirst;
import static cascading.util.Util.narrowSet;
import static java.lang.Double.POSITIVE_INFINITY;

/**
*
*/
public class ElementGraphs
  {
  private static final Logger LOG = LoggerFactory.getLogger( ElementGraphs.class );

  public static <V, E> int hashCodeIgnoreAnnotations( Graph<V, E> graph )
    {
    int hash = graph.vertexSet().hashCode();

    for( E e : graph.edgeSet() )
      {
      int part = e.hashCode();

      int source = graph.getEdgeSource( e ).hashCode();
      int target = graph.getEdgeTarget( e ).hashCode();

      // see http://en.wikipedia.org/wiki/Pairing_function (VK);
      int pairing =
        ( ( source + target )
          * ( source + target + 1 ) / 2 ) + target;

      part = ( 27 * part ) + pairing;

      long weight = (long) graph.getEdgeWeight( e );
      part = ( 27 * part ) + (int) ( weight ^ ( weight >>> 32 ) );

      hash += part;
      }

    return hash;
    }

  public static <V, E> boolean equalsIgnoreAnnotations( Graph<V, E> lhs, Graph<V, E> rhs )
    {
    if( lhs == rhs )
      return true;

    TypeUtil<Graph<V, E>> typeDecl = null;
    Graph<V, E> lhsGraph = TypeUtil.uncheckedCast( lhs, typeDecl );
    Graph<V, E> rhsGraph = TypeUtil.uncheckedCast( rhs, typeDecl );

    if( !lhsGraph.vertexSet().equals( rhsGraph.vertexSet() ) )
      return false;

    if( lhsGraph.edgeSet().size() != rhsGraph.edgeSet().size() )
      return false;

    for( E e : lhsGraph.edgeSet() )
      {
      V source = lhsGraph.getEdgeSource( e );
      V target = lhsGraph.getEdgeTarget( e );

      if( !rhsGraph.containsEdge( e ) )
        return false;

      if( !rhsGraph.getEdgeSource( e ).equals( source )
        || !rhsGraph.getEdgeTarget( e ).equals( target ) )
        return false;

      if( Math.abs( lhsGraph.getEdgeWeight( e ) - rhsGraph.getEdgeWeight( e ) ) > 10e-7 )
        return false;
      }

    return true;
    }

  public static <V, E> boolean equals( Graph<V, E> lhs, Graph<V, E> rhs )
    {
    if( !equalsIgnoreAnnotations( lhs, rhs ) )
      return false;

    if( !( lhs instanceof AnnotatedGraph ) && !( rhs instanceof AnnotatedGraph ) )
      return true;

    if( !( lhs instanceof AnnotatedGraph ) || !( rhs instanceof AnnotatedGraph ) )
      return false;

    AnnotatedGraph lhsAnnotated = (AnnotatedGraph) lhs;
    AnnotatedGraph rhsAnnotated = (AnnotatedGraph) rhs;

    if( lhsAnnotated.hasAnnotations() != rhsAnnotated.hasAnnotations() )
      return false;

    if( !lhsAnnotated.hasAnnotations() )
      return true;

    return lhsAnnotated.getAnnotations().equals( rhsAnnotated.getAnnotations() );
    }

  public static TopologicalOrderIterator<FlowElement, Scope> getTopologicalIterator( ElementGraph graph )
    {
    return new TopologicalOrderIterator<>( graph );
    }

  public static TopologicalOrderIterator<FlowElement, Scope> getReverseTopologicalIterator( ElementGraph graph )
    {
    return new TopologicalOrderIterator<>( new EdgeReversedGraph<>( graph ) );
    }

  /**
   * Method getAllShortestPathsBetween ...
   *
   * @param graph
   * @param from  of type FlowElement
   * @param to    of type FlowElement
   * @return List<GraphPath<FlowElement, Scope>>
   */
  public static <V, E> List<GraphPath<V, E>> getAllShortestPathsBetween( DirectedGraph<V, E> graph, V from, V to )
    {
    List<GraphPath<V, E>> paths = new KShortestPaths<>( graph, from, Integer.MAX_VALUE ).getPaths( to );

    if( paths == null )
      return new ArrayList<>();

    return paths;
    }

  /**
   * All paths that lead from to to without crossing a Tap/Group boundary
   *
   * @param graph
   * @param from
   * @param to
   * @return of type List
   */
  public static List<GraphPath<FlowElement, Scope>> getAllDirectPathsBetween( ElementGraph graph, FlowElement from, FlowElement to )
    {
    List<GraphPath<FlowElement, Scope>> paths = getAllShortestPathsBetween( graph, from, to );
    List<GraphPath<FlowElement, Scope>> results = new ArrayList<>( paths );

    for( GraphPath<FlowElement, Scope> path : paths )
      {
      List<FlowElement> pathVertexList = Graphs.getPathVertexList( path );

      for( int i = 1; i < pathVertexList.size() - 1; i++ ) // skip the from and to, its a Tap or Group
        {
        FlowElement flowElement = pathVertexList.get( i );

        if( flowElement instanceof Tap || flowElement instanceof Group )
          {
          results.remove( path );
          break;
          }
        }
      }

    return results;
    }

  /**
   * for every incoming stream to the splice, gets the count of paths.
   * <p/>
   * covers the case where a source may cross multiple joins to the current join and still land
   * on the lhs or rhs.
   *
   * @param graph
   * @param from
   * @param to
   * @return of type Map
   */
  public static Map<Integer, Integer> countOrderedDirectPathsBetween( ElementGraph graph, FlowElement from, Splice to )
    {
    return countOrderedDirectPathsBetween( graph, from, to, false );
    }

  public static Map<Integer, Integer> countOrderedDirectPathsBetween( ElementGraph graph, FlowElement from, Splice to, boolean skipTaps )
    {
    List<GraphPath<FlowElement, Scope>> paths = getAllDirectPathsBetween( graph, from, to );

    Map<Integer, Integer> results = new HashMap<Integer, Integer>();

    for( GraphPath<FlowElement, Scope> path : paths )
      {
      if( skipTaps && hasIntermediateTap( path, from ) )
        continue;

      pathPositionInto( results, path, to );
      }

    return results;
    }

  public static boolean isBothAccumulatedAndStreamedPath( Map<Integer, Integer> pathCounts )
    {
    return pathCounts.size() > 1 && pathCounts.containsKey( 0 );
    }

  public static boolean isOnlyStreamedPath( Map<Integer, Integer> pathCounts )
    {
    return pathCounts.size() == 1 && pathCounts.containsKey( 0 );
    }

  public static boolean isOnlyAccumulatedPath( Map<Integer, Integer> pathCounts )
    {
    return pathCounts.size() >= 1 && !pathCounts.containsKey( 0 );
    }

  private static boolean hasIntermediateTap( GraphPath<FlowElement, Scope> path, FlowElement from )
    {
    List<FlowElement> flowElements = Graphs.getPathVertexList( path );

    for( FlowElement flowElement : flowElements )
      {
      if( flowElement instanceof Tap && flowElement != from )
        return true;
      }

    return false;
    }

  private static Map<Integer, Integer> pathPositionInto( Map<Integer, Integer> results, GraphPath<FlowElement, Scope> path, Splice to )
    {
    List<Scope> scopes = path.getEdgeList();

    Scope lastScope = scopes.get( scopes.size() - 1 );

    Integer pos = to.getPipePos().get( lastScope.getName() );

    if( results.containsKey( pos ) )
      results.put( pos, results.get( pos ) + 1 );
    else
      results.put( pos, 1 );

    return results;
    }

  public static ElementSubGraph asSubGraph2( ElementGraph elementGraph, ElementGraph contractedGraph )
    {
    return new ElementSubGraph( elementGraph, findClosureViaBiConnected( elementGraph, contractedGraph ) );
    }

  public static <V, E> Set<V> findClosureViaBiConnected( DirectedGraph<V, E> full, DirectedGraph<V, E> contracted )
    {
    SimpleGraph<V, E> biConnected = (SimpleGraph<V, E>) new SimpleGraph<>( Object.class );

    Graphs.addGraph( biConnected, full );
    Graphs.addGraph( biConnected, contracted );

    BiconnectivityInspector inspector = new BiconnectivityInspector( biConnected );

    LinkedList<Set<V>> components = new LinkedList( inspector.getBiconnectedVertexComponents() );

    if( components.isEmpty() )
      throw new IllegalStateException( "no components" );

    Set<V> vertices = new HashSet<>( contracted.vertexSet() );

    for( E edge : contracted.edgeSet() )
      {
      V edgeSource = contracted.getEdgeSource( edge );
      V edgeTarget = contracted.getEdgeTarget( edge );

      ListIterator<Set<V>> iterator = components.listIterator();
      while( iterator.hasNext() )
        {
        Set<V> set = iterator.next();
        if( set.contains( edgeSource ) && set.contains( edgeTarget ) )
          {
          iterator.remove();
          vertices.addAll( set );
          }
        }
      }

    if( vertices.isEmpty() )
      throw new IllegalStateException( "no vertices" );

    return vertices;
    }

  public static ElementSubGraph asSubGraph( ElementGraph elementGraph, ElementGraph contractedGraph, Set<FlowElement> excludes )
    {
    if( elementGraph.containsVertex( Extent.head ) )
      elementGraph = new ElementMaskSubGraph( elementGraph, Extent.head, Extent.tail );

    Pair<Set<FlowElement>, Set<Scope>> pair = findClosureViaFloydWarshall( elementGraph, contractedGraph, excludes );
    Set<FlowElement> vertices = pair.getLhs();
    Set<Scope> excludeEdges = pair.getRhs();

    Set<Scope> scopes = new HashSet<>( elementGraph.edgeSet() );
    scopes.removeAll( excludeEdges );

    return new ElementSubGraph( elementGraph, vertices, scopes );
    }

  public static <V, E> Pair<Set<V>, Set<E>> findClosureViaFloydWarshall( DirectedGraph<V, E> full, DirectedGraph<V, E> contracted )
    {
    return findClosureViaFloydWarshall( full, contracted, null );
    }

  public static <V, E> Pair<Set<V>, Set<E>> findClosureViaFloydWarshall( DirectedGraph<V, E> full, DirectedGraph<V, E> contracted, Set<V> excludes )
    {
    Set<V> vertices = new HashSet<>( contracted.vertexSet() );
    LinkedList<V> allVertices = new LinkedList<>( full.vertexSet() );

    allVertices.removeAll( vertices );

    Set<E> excludeEdges = new HashSet<>();

    // prevent distinguished elements from being included inside the sub-graph
    if( excludes != null )
      {
      for( V v : excludes )
        {
        if( !full.containsVertex( v ) )
          continue;

        excludeEdges.addAll( full.incomingEdgesOf( v ) );
        excludeEdges.addAll( full.outgoingEdgesOf( v ) );
        }
      }

    for( V v : contracted.vertexSet() )
      {
      if( contracted.inDegreeOf( v ) == 0 )
        excludeEdges.addAll( full.incomingEdgesOf( v ) );
      }

    for( V v : contracted.vertexSet() )
      {
      if( contracted.outDegreeOf( v ) == 0 )
        excludeEdges.addAll( full.outgoingEdgesOf( v ) );
      }

    DirectedGraph<V, E> disconnected = disconnectExtentsAndExclude( full, excludeEdges );

    FloydWarshallShortestPaths<V, E> paths = new FloydWarshallShortestPaths<>( disconnected );

    for( E edge : contracted.edgeSet() )
      {
      V edgeSource = contracted.getEdgeSource( edge );
      V edgeTarget = contracted.getEdgeTarget( edge );

      ListIterator<V> iterator = allVertices.listIterator();
      while( iterator.hasNext() )
        {
        V vertex = iterator.next();

        if( !isBetween( paths, edgeSource, edgeTarget, vertex ) )
          continue;

        vertices.add( vertex );
        iterator.remove();
        }
      }

    return new Pair<>( vertices, excludeEdges );
    }

  private static <V, E> DirectedGraph<V, E> disconnectExtentsAndExclude( DirectedGraph<V, E> full, Set<E> withoutEdges )
    {
    DirectedGraph<V, E> copy = (DirectedGraph<V, E>) new SimpleDirectedGraph<>( Object.class );

    Graphs.addAllVertices( copy, full.vertexSet() );

    copy.removeVertex( (V) Extent.head );
    copy.removeVertex( (V) Extent.tail );

    Set<E> edges = full.edgeSet();

    if( !withoutEdges.isEmpty() )
      {
      edges = new HashSet<>( edges );
      edges.removeAll( withoutEdges );
      }

    Graphs.addAllEdges( copy, full, edges );

    return copy;
    }

  private static <V, E> boolean isBetween( FloydWarshallShortestPaths<V, E> paths, V edgeSource, V edgeTarget, V vertex )
    {
    return paths.shortestDistance( edgeSource, vertex ) != POSITIVE_INFINITY && paths.shortestDistance( vertex, edgeTarget ) != POSITIVE_INFINITY;
    }

  public static ElementSubGraph asSubGraphKS( ElementGraph elementGraph, ElementGraph contractedGraph )
    {
    return new ElementSubGraph( elementGraph, findClosureViaKShortest( elementGraph, contractedGraph ) );
    }

  public static <V, E> Set<V> findClosureViaKShortest( DirectedGraph<V, E> elementGraph, DirectedGraph<V, E> contractedGraph )
    {
    // this is bad news, shortest paths is non-linear

    Set<V> allVertices = new HashSet<>( contractedGraph.vertexSet() ); // if no edges, we have the vertices
    Set<E> edges = contractedGraph.edgeSet();

    for( E edge : edges )
      {
      V lhs = contractedGraph.getEdgeSource( edge );
      V rhs = contractedGraph.getEdgeTarget( edge );

      Set<V> otherVertices = new HashSet<>( contractedGraph.vertexSet() );

      otherVertices.remove( lhs );
      otherVertices.remove( rhs );

      List<GraphPath<V, E>> between = getAllShortestPathsBetween( elementGraph, lhs, rhs );

      for( GraphPath<V, E> graphPath : between )
        {
        List<V> pathVertices = Graphs.getPathVertexList( graphPath );

        // do not include a path if it crosses a distinguished element from the contracted graph
        if( Collections.disjoint( pathVertices, otherVertices ) )
          allVertices.addAll( pathVertices );
        }
      }

    return allVertices;
    }

  public static void removeAndContract( ElementGraph elementGraph, FlowElement flowElement )
    {
    LOG.debug( "removing element, contracting edge : " + flowElement );

    Set<Scope> incomingScopes = elementGraph.incomingEdgesOf( flowElement );

    boolean contractIncoming = true;

    if( !contractIncoming )
      {
      if( incomingScopes.size() != 1 )
        throw new IllegalStateException( "flow element:" + flowElement + ", has multiple input paths: " + incomingScopes.size() );
      }

    boolean isJoin = flowElement instanceof Splice && ( (Splice) flowElement ).isJoin();

    for( Scope incoming : incomingScopes )
      {
      Set<Scope> outgoingScopes = elementGraph.outgoingEdgesOf( flowElement );

      // source -> incoming -> flowElement -> outgoing -> target
      FlowElement source = elementGraph.getEdgeSource( incoming );

      for( Scope outgoing : outgoingScopes )
        {
        FlowElement target = elementGraph.getEdgeTarget( outgoing );

        boolean isNonBlocking = outgoing.isNonBlocking();

        if( isJoin )
          isNonBlocking = isNonBlocking && incoming.isNonBlocking();

        Scope scope = new Scope( outgoing );

        // unsure if necessary since we track blocking independently
        // when removing a pipe, pull ordinal up to tap
        // when removing a Splice retain ordinal
        if( flowElement instanceof Splice )
          scope.setOrdinal( incoming.getOrdinal() );
        else
          scope.setOrdinal( outgoing.getOrdinal() );

        scope.setNonBlocking( isNonBlocking );
        scope.addPriorNames( incoming, outgoing ); // not copied
        elementGraph.addEdge( source, target, scope );
        }
      }

    elementGraph.removeVertex( flowElement );
    }

  public static boolean printElementGraph( String filename, final DirectedGraph<FlowElement, Scope> graph, final PlatformInfo platformInfo )
    {
    try
      {
      File parentFile = new File( filename ).getParentFile();

      if( parentFile != null && !parentFile.exists() )
        parentFile.mkdirs();

      Writer writer = new FileWriter( filename );

      Util.writeDOT( writer, graph,
        new IntegerNameProvider<FlowElement>(),
        new FlowElementVertexNameProvider( graph, platformInfo ),
        new ScopeEdgeNameProvider(),
        new VertexAttributeProvider(), new EdgeAttributeProvider() );

      writer.close();
      return true;
      }
    catch( IOException exception )
      {
      LOG.error( "failed printing graph to: {}, with exception: {}", filename, exception );
      }

    return false;
    }

  public static void insertFlowElementAfter( ElementGraph elementGraph, FlowElement previousElement, FlowElement flowElement )
    {
    Set<Scope> outgoing = new HashSet<>( elementGraph.outgoingEdgesOf( previousElement ) );

    elementGraph.addVertex( flowElement );

    String name = previousElement.toString();

    if( previousElement instanceof Pipe )
      name = ( (Pipe) previousElement ).getName();

    elementGraph.addEdge( previousElement, flowElement, new Scope( name ) );

    for( Scope scope : outgoing )
      {
      FlowElement target = elementGraph.getEdgeTarget( scope );
      Scope foundScope = elementGraph.removeEdge( previousElement, target ); // remove scope

      if( foundScope != scope )
        throw new IllegalStateException( "did not remove proper scope" );

      elementGraph.addEdge( flowElement, target, scope ); // add scope back
      }
    }

  public static void insertFlowElementBefore( ElementGraph graph, FlowElement nextElement, FlowElement flowElement )
    {
    Set<Scope> incoming = new HashSet<>( graph.incomingEdgesOf( nextElement ) );

    graph.addVertex( flowElement );

    String name = nextElement.toString();

    if( nextElement instanceof Pipe )
      name = ( (Pipe) nextElement ).getName();

    graph.addEdge( flowElement, nextElement, new Scope( name ) );

    for( Scope scope : incoming )
      {
      FlowElement target = graph.getEdgeSource( scope );
      Scope foundScope = graph.removeEdge( target, nextElement ); // remove scope

      if( foundScope != scope )
        throw new IllegalStateException( "did not remove proper scope" );

      graph.addEdge( target, flowElement, scope ); // add scope back
      }
    }

  public static void addSources( BaseFlowStep flowStep, ElementGraph elementGraph, Set<Tap> sources )
    {
    for( Tap tap : sources )
      {
      for( Scope scope : elementGraph.outgoingEdgesOf( tap ) )
        flowStep.addSource( scope.getName(), tap );
      }
    }

  public static Set<Tap> findSources( ElementGraph elementGraph )
    {
    return findSources( elementGraph, Tap.class );
    }

  public static <F extends FlowElement> Set<F> findSources( ElementGraph elementGraph, Class<F> type )
    {
    if( elementGraph.containsVertex( Extent.head ) )
      return narrowSet( type, Graphs.successorListOf( elementGraph, Extent.head ) );

    SubGraphIterator iterator = new ExpressionSubGraphIterator(
      new ExpressionGraph( SearchOrder.Topological, new FlowElementExpression( ElementCapture.Primary, type, TypeExpression.Topo.Head ) ),
      elementGraph
    );

    return narrowSet( type, getAllVertices( iterator ) );
    }

  public static <F extends FlowElement> Set<F> findSinks( ElementGraph elementGraph, Class<F> type )
    {
    if( elementGraph.containsVertex( Extent.tail ) )
      return narrowSet( type, Graphs.predecessorListOf( elementGraph, Extent.tail ) );

    SubGraphIterator iterator = new ExpressionSubGraphIterator(
      new ExpressionGraph( SearchOrder.ReverseTopological, new FlowElementExpression( ElementCapture.Primary, type, TypeExpression.Topo.Tail ) ),
      elementGraph
    );

    return narrowSet( type, getAllVertices( iterator ) );
    }

  public static void addSinks( BaseFlowStep flowStep, ElementGraph elementGraph, Set<Tap> sinks )
    {
    for( Tap tap : sinks )
      {
      for( Scope scope : elementGraph.incomingEdgesOf( tap ) )
        flowStep.addSink( scope.getName(), tap );
      }
    }

  public static Set<Tap> findSinks( ElementGraph elementGraph )
    {
    return findSinks( elementGraph, Tap.class );
    }

  public static Set<Group> findAllGroups( ElementGraph elementGraph )
    {
    SubGraphIterator iterator = new ExpressionSubGraphIterator(
      new ExpressionGraph( SearchOrder.Topological, new FlowElementExpression( ElementCapture.Primary, Group.class ) ),
      elementGraph
    );

    return narrowSet( Group.class, getAllVertices( iterator ) );
    }

  public static Set<HashJoin> findAllHashJoins( ElementGraph elementGraph )
    {
    SubGraphIterator iterator = new ExpressionSubGraphIterator(
      new ExpressionGraph( SearchOrder.Topological, new FlowElementExpression( ElementCapture.Primary, HashJoin.class ) ),
      elementGraph
    );

    return narrowSet( HashJoin.class, getAllVertices( iterator ) );
    }

  private static Set<FlowElement> getAllVertices( SubGraphIterator iterator )
    {
    Set<FlowElement> vertices = new HashSet<>();

    while( iterator.hasNext() )
      vertices.addAll( iterator.next().vertexSet() );

    return vertices;
    }

  public static void replaceElementWith( ElementGraph elementGraph, FlowElement replace, FlowElement replaceWith )
    {
    Set<Scope> incoming = new HashSet<Scope>( elementGraph.incomingEdgesOf( replace ) );
    Set<Scope> outgoing = new HashSet<Scope>( elementGraph.outgoingEdgesOf( replace ) );

    if( !elementGraph.containsVertex( replaceWith ) )
      elementGraph.addVertex( replaceWith );

    for( Scope scope : incoming )
      {
      FlowElement source = elementGraph.getEdgeSource( scope );
      elementGraph.removeEdge( source, replace ); // remove scope

      // drop edge between, if any
      if( source != replaceWith )
        elementGraph.addEdge( source, replaceWith, scope ); // add scope back
      }

    for( Scope scope : outgoing )
      {
      FlowElement target = elementGraph.getEdgeTarget( scope );
      elementGraph.removeEdge( replace, target ); // remove scope

      // drop edge between, if any
      if( target != replaceWith )
        elementGraph.addEdge( replaceWith, target, scope ); // add scope back
      }

    elementGraph.removeVertex( replace );
    }

  public static Pipe findFirstPipeNamed( ElementGraph elementGraph, String name )
    {
    Iterator<FlowElement> iterator = getTopologicalIterator( elementGraph );

    return find( name, iterator );
    }

  public static Pipe findLastPipeNamed( ElementGraph elementGraph, String name )
    {
    Iterator<FlowElement> iterator = getReverseTopologicalIterator( elementGraph );

    return find( name, iterator );
    }

  private static Pipe find( String name, Iterator<FlowElement> iterator )
    {
    while( iterator.hasNext() )
      {
      FlowElement flowElement = iterator.next();

      if( flowElement instanceof Pipe && ( (Pipe) flowElement ).getName().equals( name ) )
        return (Pipe) flowElement;
      }

    return null;
    }

  public static boolean removeBranchContaining( ElementGraph elementGraph, FlowElement flowElement )
    {
    Set<FlowElement> branch = new LinkedHashSet<>();

    walkUp( branch, elementGraph, flowElement );

    walkDown( branch, elementGraph, flowElement );

    if( branch.isEmpty() )
      return false;

    for( FlowElement element : branch )
      elementGraph.removeVertex( element );

    return true;
    }

  public static boolean removeBranchBetween( ElementGraph elementGraph, FlowElement first, FlowElement second, boolean inclusive )
    {
    Set<FlowElement> branch = new LinkedHashSet<>( Arrays.asList( first, second ) );

    walkDown( branch, elementGraph, first );

    if( !inclusive )
      {
      branch.remove( first );
      branch.remove( second );
      }

    if( branch.isEmpty() )
      return false;

    for( FlowElement element : branch )
      elementGraph.removeVertex( element );

    return true;
    }

  private static void walkDown( Set<FlowElement> branch, ElementGraph elementGraph, FlowElement flowElement )
    {
    FlowElement current;
    current = flowElement;

    while( true )
      {
      if( !branch.contains( current ) && ( elementGraph.inDegreeOf( current ) != 1 || elementGraph.outDegreeOf( current ) != 1 ) )
        break;

      branch.add( current );

      FlowElement element = elementGraph.getEdgeTarget( getFirst( elementGraph.outgoingEdgesOf( current ) ) );

      if( element instanceof Extent || branch.contains( element ) )
        break;

      current = element;
      }
    }

  private static void walkUp( Set<FlowElement> branch, ElementGraph elementGraph, FlowElement flowElement )
    {
    FlowElement current = flowElement;

    while( true )
      {
      if( elementGraph.inDegreeOf( current ) != 1 || elementGraph.outDegreeOf( current ) != 1 )
        break;

      branch.add( current );

      FlowElement element = elementGraph.getEdgeSource( getFirst( elementGraph.incomingEdgesOf( current ) ) );

      if( element instanceof Extent || branch.contains( element ) )
        break;

      current = element;
      }
    }

  private static class FlowElementVertexNameProvider implements VertexNameProvider<FlowElement>
    {
    private final DirectedGraph<FlowElement, Scope> graph;
    private final PlatformInfo platformInfo;

    public FlowElementVertexNameProvider( DirectedGraph<FlowElement, Scope> graph, PlatformInfo platformInfo )
      {
      this.graph = graph;
      this.platformInfo = platformInfo;
      }

    public String getVertexName( FlowElement object )
      {
      if( object instanceof Extent ) // is head/tail
        {
        String result = object.toString().replaceAll( "\"", "\'" );

        if( object == Extent.tail )
          return result;

        String versionString = Version.getRelease();

        if( platformInfo != null )
          versionString = ( versionString == null ? "" : versionString + "|" ) + platformInfo;

        return "{" + ( versionString == null ? result : result + "|" + versionString ) + "}";
        }

      String label;

      Iterator<Scope> iterator = graph.outgoingEdgesOf( object ).iterator();

      if( object instanceof Tap || !iterator.hasNext() )
        {
        label = object.toString().replaceAll( "\"", "\'" ).replaceAll( "(\\)|\\])(\\[)", "$1|$2" ).replaceAll( "(^[^(\\[]+)(\\(|\\[)", "$1|$2" );
        }
      else
        {
        Scope scope = iterator.next();

        label = ( (Pipe) object ).print( scope ).replaceAll( "\"", "\'" ).replaceAll( "(\\)|\\])(\\[)", "$1|$2" ).replaceAll( "(^[^(\\[]+)(\\(|\\[)", "$1|$2" );
        }

      label = "{" + label.replaceAll( "\\{", "\\\\{" ).replaceAll( "\\}", "\\\\}" ).replaceAll( ">", "\\\\>" ) + "}";

      if( !( graph instanceof AnnotatedGraph ) || !( (AnnotatedGraph) graph ).hasAnnotations() )
        return label;

      Set<Enum> annotations = ( (AnnotatedGraph) graph ).getAnnotations().getKeysFor( object );

      if( !annotations.isEmpty() )
        label += "|{" + Util.join( annotations, "|" ) + "}";

      return label;
      }
    }

  private static class ScopeEdgeNameProvider implements EdgeNameProvider<Scope>
    {
    public String getEdgeName( Scope object )
      {
      return object.toString().replaceAll( "\"", "\'" ).replaceAll( "\n", "\\\\n" ); // fix for newlines in graphviz
      }
    }

  private static class VertexAttributeProvider implements ComponentAttributeProvider<FlowElement>
    {
    static Map<String, String> defaultNode = new HashMap<String, String>()
    {
    {put( "shape", "Mrecord" );}
    };

    public VertexAttributeProvider()
      {
      }

    @Override
    public Map<String, String> getComponentAttributes( FlowElement object )
      {
      return defaultNode;
      }
    }

  private static class EdgeAttributeProvider implements ComponentAttributeProvider<Scope>
    {
    static Map<String, String> attributes = new HashMap<String, String>()
    {
    {put( "style", "dotted" );}

    {put( "arrowhead", "dot" );}
    };

    @Override
    public Map<String, String> getComponentAttributes( Scope scope )
      {
      if( scope.isNonBlocking() )
        return null;

      return attributes;
      }
    }
  }
TOP

Related Classes of cascading.flow.planner.graph.ElementGraphs$EdgeAttributeProvider

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.