001/*
002 * Copyright (c) 2007-2015 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  }