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