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 }