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 }