    001/*
    002 * Copyright (c) 2007-2016 Concurrent, Inc. All Rights Reserved.
    003 *
    004 * Project and contact information: http://www.cascading.org/
    005 *
    006 * This file is part of the Cascading project.
    007 *
    008 * Licensed under the Apache License, Version 2.0 (the "License");
    009 * you may not use this file except in compliance with the License.
    010 * You may obtain a copy of the License at
    011 *
    012 *     http://www.apache.org/licenses/LICENSE-2.0
    013 *
    014 * Unless required by applicable law or agreed to in writing, software
    015 * distributed under the License is distributed on an "AS IS" BASIS,
    016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    017 * See the License for the specific language governing permissions and
    018 * limitations under the License.
    019 */
    020
    021package cascading.flow.planner.iso.expression;
    022
    023import java.io.File;
    024import java.io.FileWriter;
    025import java.io.IOException;
    026import java.io.Writer;
    027
    028import cascading.flow.planner.PlannerContext;
    029import cascading.flow.planner.Scope;
    030import cascading.flow.planner.graph.ElementGraph;
    031import cascading.flow.planner.iso.finder.SearchOrder;
    032import cascading.util.Util;
    033import cascading.util.jgrapht.DOTExporter;
    034import cascading.util.jgrapht.IntegerNameProvider;
    035import cascading.util.jgrapht.StringEdgeNameProvider;
    036import cascading.util.jgrapht.StringNameProvider;
    037import org.jgrapht.graph.ClassBasedEdgeFactory;
    038import org.jgrapht.graph.DirectedMultigraph;
    039import org.slf4j.Logger;
    040import org.slf4j.LoggerFactory;
    041
    042/**
    043 *
    044 */
    045public class ExpressionGraph
    046  {
    047  private static final Logger LOG = LoggerFactory.getLogger( ExpressionGraph.class );
    048
    049  private final SearchOrder searchOrder;
    050  private final DirectedMultigraph<ElementExpression, ScopeExpression> graph;
    051
    052  private boolean allowNonRecursiveMatching;
    053
    054  public ExpressionGraph()
    055    {
    056    this.searchOrder = SearchOrder.ReverseTopological;
    057    this.graph = new DirectedMultigraph( new ClassBasedEdgeFactory( PathScopeExpression.class ) );
    058    this.allowNonRecursiveMatching = true;
    059    }
    060
    061  public ExpressionGraph( boolean allowNonRecursiveMatching )
    062    {
    063    this();
    064    this.allowNonRecursiveMatching = allowNonRecursiveMatching;
    065    }
    066
    067  public ExpressionGraph( ElementExpression... matchers )
    068    {
    069    this();
    070    arcs( matchers );
    071    }
    072
    073  public ExpressionGraph( SearchOrder searchOrder, ElementExpression... matchers )
    074    {
    075    this( searchOrder );
    076    arcs( matchers );
    077    }
    078
    079  public ExpressionGraph( SearchOrder searchOrder )
    080    {
    081    this( searchOrder, true );
    082    }
    083
    084  public ExpressionGraph( SearchOrder searchOrder, boolean allowNonRecursiveMatching )
    085    {
    086    this.searchOrder = searchOrder;
    087    this.graph = new DirectedMultigraph( new ClassBasedEdgeFactory( PathScopeExpression.class ) );
    088    this.allowNonRecursiveMatching = allowNonRecursiveMatching;
    089    }
    090
    091  public DirectedMultigraph<ElementExpression, ScopeExpression> getGraph()
    092    {
    093    return graph;
    094    }
    095
    096  public SearchOrder getSearchOrder()
    097    {
    098    return searchOrder;
    099    }
    100
    101  public boolean supportsNonRecursiveMatch()
    102    {
    103    return allowNonRecursiveMatching &&
    104      getGraph().vertexSet().size() == 1 &&
    105      Util.getFirst( getGraph().vertexSet() ).getCapture() == ElementCapture.Primary;
    106    }
    107
    108  public ExpressionGraph setAllowNonRecursiveMatching( boolean allowNonRecursiveMatching )
    109    {
    110    this.allowNonRecursiveMatching = allowNonRecursiveMatching;
    111
    112    return this;
    113    }
    114
    115  public ExpressionGraph arcs( ElementExpression... matchers )
    116    {
    117    ElementExpression lhs = null;
    118
    119    for( ElementExpression matcher : matchers )
    120      {
    121      graph.addVertex( matcher );
    122
    123      if( lhs != null )
    124        graph.addEdge( lhs, matcher );
    125
    126      lhs = matcher;
    127      }
    128
    129    return this;
    130    }
    131
    132  public ExpressionGraph arc( ElementExpression lhsMatcher, ScopeExpression scopeMatcher, ElementExpression rhsMatcher )
    133    {
    134    graph.addVertex( lhsMatcher );
    135    graph.addVertex( rhsMatcher );
    136
    137    // can never re-use edges, must be wrapped
    138    graph.addEdge( lhsMatcher, rhsMatcher, new DelegateScopeExpression( scopeMatcher ) );
    139
    140    return this;
    141    }
    142
    143  public void writeDOT( String filename )
    144    {
    145    try
    146      {
    147      File parentFile = new File( filename ).getParentFile();
    148
    149      if( parentFile != null && !parentFile.exists() )
    150        parentFile.mkdirs();
    151
    152      Writer writer = new FileWriter( filename );
    153
    154      new DOTExporter( new IntegerNameProvider(), new StringNameProvider(), new StringEdgeNameProvider() ).export( writer, getGraph() );
    155
    156      writer.close();
    157
    158      Util.writePDF( filename );
    159      }
    160    catch( IOException exception )
    161      {
    162      LOG.error( "failed printing expression graph to: {}, with exception: {}", filename, exception );
    163      }
    164    }
    165
    166  public static ScopeExpression unwind( ScopeExpression scopeExpression )
    167    {
    168    if( scopeExpression instanceof DelegateScopeExpression )
    169      return ( (DelegateScopeExpression) scopeExpression ).delegate;
    170
    171    return scopeExpression;
    172    }
    173
    174  private static class DelegateScopeExpression extends ScopeExpression
    175    {
    176    ScopeExpression delegate;
    177
    178    protected DelegateScopeExpression( ScopeExpression delegate )
    179      {
    180      this.delegate = delegate;
    181      }
    182
    183    @Override
    184    public boolean acceptsAll()
    185      {
    186      return delegate.acceptsAll();
    187      }
    188
    189    @Override
    190    public boolean appliesToAllPaths()
    191      {
    192      return delegate.appliesToAllPaths();
    193      }
    194
    195    @Override
    196    public boolean appliesToAnyPath()
    197      {
    198      return delegate.appliesToAnyPath();
    199      }
    200
    201    @Override
    202    public boolean appliesToEachPath()
    203      {
    204      return delegate.appliesToEachPath();
    205      }
    206
    207    @Override
    208    public boolean applies( PlannerContext plannerContext, ElementGraph elementGraph, Scope scope )
    209      {
    210      return delegate.applies( plannerContext, elementGraph, scope );
    211      }
    212
    213    @Override
    214    public String toString()
    215      {
    216      return delegate.toString();
    217      }
    218    }
    219  }