001 /* 002 * Copyright (c) 2007-2014 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 021 package cascading.flow.planner; 022 023 import java.io.Serializable; 024 import java.util.HashSet; 025 import java.util.Map; 026 import java.util.Set; 027 028 import cascading.tuple.Fields; 029 030 import static cascading.tuple.Fields.asDeclaration; 031 032 /** Class Scope is an internal representation of the linkages between operations. */ 033 public class Scope implements Serializable 034 { 035 /** Enum Kind */ 036 static public enum Kind 037 { 038 TAP, EACH, EVERY, GROUP, SPLICE 039 } 040 041 /** Field name */ 042 private String name; 043 /** Field kind */ 044 private Kind kind; 045 /** Field incomingPassThroughFields */ 046 private Fields incomingPassThroughFields; 047 /** Field remainderPassThroughFields */ 048 private Fields remainderPassThroughFields; 049 050 /** Field argumentSelector */ 051 private Fields operationArgumentFields; 052 /** Field declaredFields */ 053 private Fields operationDeclaredFields; // fields declared by the operation 054 /** Field isGroupBy */ 055 private boolean isGroupBy; 056 057 /** Field groupingSelectors */ 058 private Map<String, Fields> keySelectors; 059 /** Field sortingSelectors */ 060 private Map<String, Fields> sortingSelectors; 061 062 /** Field outGroupingSelector */ 063 private Fields outGroupingSelector; 064 /** Field outGroupingFields */ 065 private Fields outGroupingFields; // all key fields 066 067 /** Field outValuesSelector */ 068 private Fields outValuesSelector; 069 /** Field outValuesFields */ 070 private Fields outValuesFields; // all value fields, includes keys 071 072 /** Default constructor. */ 073 public Scope() 074 { 075 } 076 077 /** 078 * Copy constructor 079 * 080 * @param scope of type Scope 081 */ 082 public Scope( Scope scope ) 083 { 084 this.name = scope.getName(); 085 copyFields( scope ); 086 } 087 088 /** 089 * Tap constructor 090 * 091 * @param outFields of type Fields 092 */ 093 public Scope( Fields outFields ) 094 { 095 this.kind = Kind.TAP; 096 097 if( outFields == null ) 098 throw new IllegalArgumentException( "fields may not be null" ); 099 100 this.outGroupingFields = outFields; 101 this.outValuesFields = outFields; 102 } 103 104 /** 105 * Constructor Scope creates a new Scope instance. Used by classes Each and Every. 106 * 107 * @param name of type String 108 * @param kind of type Kind 109 * @param incomingPassThroughFields // * @param remainderPassThroughFields of type Fields 110 * @param operationArgumentFields of type Fields 111 * @param operationDeclaredFields of type Fields 112 * @param outGroupingFields of type Fields 113 * @param outValuesFields of type Fields 114 */ 115 public Scope( String name, Kind kind, Fields incomingPassThroughFields, Fields remainderPassThroughFields, Fields operationArgumentFields, Fields operationDeclaredFields, Fields outGroupingFields, Fields outValuesFields ) 116 { 117 this.name = name; 118 this.kind = kind; 119 this.incomingPassThroughFields = incomingPassThroughFields; 120 this.remainderPassThroughFields = remainderPassThroughFields; 121 this.operationArgumentFields = operationArgumentFields; 122 this.operationDeclaredFields = operationDeclaredFields; 123 124 if( outGroupingFields == null ) 125 throw new IllegalArgumentException( "grouping may not be null" ); 126 127 if( outValuesFields == null ) 128 throw new IllegalArgumentException( "values may not be null" ); 129 130 if( kind == Kind.EACH ) 131 { 132 this.outGroupingSelector = outGroupingFields; 133 this.outGroupingFields = asDeclaration( outGroupingFields ); 134 this.outValuesSelector = outValuesFields; 135 this.outValuesFields = asDeclaration( outValuesFields ); 136 } 137 else if( kind == Kind.EVERY ) 138 { 139 this.outGroupingSelector = outGroupingFields; 140 this.outGroupingFields = asDeclaration( outGroupingFields ); 141 this.outValuesSelector = outValuesFields; 142 this.outValuesFields = asDeclaration( outValuesFields ); 143 } 144 else 145 { 146 throw new IllegalArgumentException( "may not use the constructor for kind: " + kind ); 147 } 148 } 149 150 /** 151 * Constructor Scope creates a new Scope instance. Used by the Group class. 152 * 153 * @param name of type String 154 * @param operationDeclaredFields of type Fields 155 * @param outGroupingFields of type Fields 156 * @param keySelectors of type Map<String, Fields> 157 * @param sortingSelectors of type Fields 158 * @param outValuesFields of type Fields 159 * @param isGroupBy of type boolean 160 */ 161 public Scope( String name, Fields operationDeclaredFields, Fields outGroupingFields, Map<String, Fields> keySelectors, Map<String, Fields> sortingSelectors, Fields outValuesFields, boolean isGroupBy ) 162 { 163 this.name = name; 164 this.kind = Kind.GROUP; 165 this.isGroupBy = isGroupBy; 166 167 if( keySelectors == null ) 168 throw new IllegalArgumentException( "grouping may not be null" ); 169 170 if( outValuesFields == null ) 171 throw new IllegalArgumentException( "values may not be null" ); 172 173 this.operationDeclaredFields = operationDeclaredFields; 174 this.outGroupingFields = asDeclaration( outGroupingFields ); 175 this.keySelectors = keySelectors; 176 this.sortingSelectors = sortingSelectors; // null ok 177 this.outValuesFields = asDeclaration( outValuesFields ); 178 } 179 180 /** 181 * Constructor Scope creates a new Scope instance. 182 * 183 * @param name of type String 184 */ 185 public Scope( String name ) 186 { 187 this.name = name; 188 } 189 190 /** 191 * Method isGroup returns true if this Scope object represents a Group. 192 * 193 * @return the group (type boolean) of this Scope object. 194 */ 195 public boolean isGroup() 196 { 197 return kind == Kind.GROUP; 198 } 199 200 /** 201 * Method isEach returns true if this Scope object represents an Each. 202 * 203 * @return the each (type boolean) of this Scope object. 204 */ 205 public boolean isEach() 206 { 207 return kind == Kind.EACH; 208 } 209 210 /** 211 * Method isEvery returns true if this Scope object represents an Every. 212 * 213 * @return the every (type boolean) of this Scope object. 214 */ 215 public boolean isEvery() 216 { 217 return kind == Kind.EVERY; 218 } 219 220 /** 221 * Method isTap returns true if this Scope object represents a Tap. 222 * 223 * @return the tap (type boolean) of this Scope object. 224 */ 225 public boolean isTap() 226 { 227 return kind == Kind.TAP; 228 } 229 230 /** 231 * Method getName returns the name of this Scope object. 232 * 233 * @return the name (type String) of this Scope object. 234 */ 235 public String getName() 236 { 237 return name; 238 } 239 240 /** 241 * Method setName sets the name of this Scope object. 242 * 243 * @param name the name of this Scope object. 244 */ 245 public void setName( String name ) 246 { 247 this.name = name; 248 } 249 250 /** 251 * Method getRemainderFields returns the remainderFields of this Scope object. 252 * 253 * @return the remainderFields (type Fields) of this Scope object. 254 */ 255 public Fields getRemainderPassThroughFields() 256 { 257 return remainderPassThroughFields; 258 } 259 260 /** 261 * Method getArgumentSelector returns the argumentSelector of this Scope object. 262 * 263 * @return the argumentSelector (type Fields) of this Scope object. 264 */ 265 public Fields getArgumentsSelector() 266 { 267 return operationArgumentFields; 268 } 269 270 /** 271 * Method getArguments returns the arguments of this Scope object. 272 * 273 * @return the arguments (type Fields) of this Scope object. 274 */ 275 public Fields getArgumentsDeclarator() 276 { 277 return asDeclaration( operationArgumentFields ); 278 } 279 280 /** 281 * Method getDeclaredFields returns the declaredFields of this Scope object. 282 * 283 * @return the declaredFields (type Fields) of this Scope object. 284 */ 285 public Fields getOperationDeclaredFields() 286 { 287 return operationDeclaredFields; 288 } 289 290 /** 291 * Method getGroupingSelectors returns the groupingSelectors of this Scope object. 292 * 293 * @return the groupingSelectors (type Map<String, Fields>) of this Scope object. 294 */ 295 public Map<String, Fields> getKeySelectors() 296 { 297 return keySelectors; 298 } 299 300 /** 301 * Method getSortingSelectors returns the sortingSelectors of this Scope object. 302 * 303 * @return the sortingSelectors (type Map<String, Fields>) of this Scope object. 304 */ 305 public Map<String, Fields> getSortingSelectors() 306 { 307 return sortingSelectors; 308 } 309 310 /** 311 * Method getOutGroupingSelector returns the outGroupingSelector of this Scope object. 312 * 313 * @return the outGroupingSelector (type Fields) of this Scope object. 314 */ 315 public Fields getOutGroupingSelector() 316 { 317 return outGroupingSelector; 318 } 319 320 public Fields getIncomingTapFields() 321 { 322 if( isEvery() ) 323 return getOutGroupingFields(); 324 else 325 return getOutValuesFields(); 326 } 327 328 public Fields getIncomingFunctionArgumentFields() 329 { 330 if( isEvery() ) 331 return getOutGroupingFields(); 332 else 333 return getOutValuesFields(); 334 } 335 336 public Fields getIncomingFunctionPassThroughFields() 337 { 338 if( isEvery() ) 339 return getOutGroupingFields(); 340 else 341 return getOutValuesFields(); 342 } 343 344 public Fields getIncomingAggregatorArgumentFields() 345 { 346 if( isEach() || isTap() ) 347 throw new IllegalStateException( "Every cannot follow a Tap or an Each" ); 348 349 return getOutValuesFields(); 350 } 351 352 public Fields getIncomingAggregatorPassThroughFields() 353 { 354 if( isEach() || isTap() ) 355 throw new IllegalStateException( "Every cannot follow a Tap or an Each" ); 356 357 return getOutGroupingFields(); 358 } 359 360 public Fields getIncomingBufferArgumentFields() 361 { 362 if( isEach() || isTap() ) 363 throw new IllegalStateException( "Every cannot follow a Tap or an Each" ); 364 365 return getOutValuesFields(); 366 } 367 368 public Fields getIncomingBufferPassThroughFields() 369 { 370 if( isEach() || isTap() ) 371 throw new IllegalStateException( "Every cannot follow a Tap or an Each" ); 372 373 return getOutValuesFields(); 374 } 375 376 public Fields getIncomingSpliceFields() 377 { 378 if( isEvery() ) 379 return getOutGroupingFields(); 380 else 381 return getOutValuesFields(); 382 } 383 384 /** 385 * Method getOutGroupingFields returns the outGroupingFields of this Scope object. 386 * 387 * @return the outGroupingFields (type Fields) of this Scope object. 388 */ 389 public Fields getOutGroupingFields() 390 { 391 if( !isGroup() ) 392 return outGroupingFields; 393 394 Fields first = keySelectors.values().iterator().next(); 395 396 // if more than one, this is a merge, so same key names are expected 397 if( keySelectors.size() == 1 || isGroup() && isGroupBy ) 398 return first; 399 400 // handling CoGroup only 401 402 // if given by user as resultGroupFields 403 if( outGroupingFields != null ) 404 return outGroupingFields; 405 406 // todo throw an exception if we make it this far 407 408 // if all have the same names, then use for grouping 409 Set<Fields> set = new HashSet<Fields>( keySelectors.values() ); 410 411 if( set.size() == 1 ) 412 return first; 413 414 return Fields.size( first.size() ); 415 } 416 417 public Fields getOutGroupingValueFields() 418 { 419 return getOutValuesFields().subtract( getOutGroupingFields() ); 420 } 421 422 /** 423 * Method getOutValuesSelector returns the outValuesSelector of this Scope object. 424 * 425 * @return the outValuesSelector (type Fields) of this Scope object. 426 */ 427 public Fields getOutValuesSelector() 428 { 429 return outValuesSelector; 430 } 431 432 /** 433 * Method getOutValuesFields returns the outValuesFields of this Scope object. 434 * 435 * @return the outValuesFields (type Fields) of this Scope object. 436 */ 437 public Fields getOutValuesFields() 438 { 439 return outValuesFields; 440 } 441 442 /** 443 * Method copyFields copies the given Scope instance fields to this instance. 444 * 445 * @param scope of type Scope 446 */ 447 public void copyFields( Scope scope ) 448 { 449 this.kind = scope.kind; 450 this.isGroupBy = scope.isGroupBy; 451 this.incomingPassThroughFields = scope.incomingPassThroughFields; 452 this.remainderPassThroughFields = scope.remainderPassThroughFields; 453 this.operationArgumentFields = scope.operationArgumentFields; 454 this.operationDeclaredFields = scope.operationDeclaredFields; 455 this.keySelectors = scope.keySelectors; 456 this.sortingSelectors = scope.sortingSelectors; 457 this.outGroupingSelector = scope.outGroupingSelector; 458 this.outGroupingFields = scope.outGroupingFields; 459 this.outValuesSelector = scope.outValuesSelector; 460 this.outValuesFields = scope.outValuesFields; 461 } 462 463 @Override 464 public String toString() 465 { 466 if( getOutValuesFields() == null ) 467 return ""; // endpipes 468 469 StringBuffer buffer = new StringBuffer(); 470 471 if( keySelectors != null && !keySelectors.isEmpty() ) 472 { 473 for( String name : keySelectors.keySet() ) 474 { 475 if( buffer.length() != 0 ) 476 buffer.append( "," ); 477 buffer.append( name ).append( keySelectors.get( name ).printVerbose() ); 478 } 479 480 buffer.append( "\n" ); 481 } 482 483 if( outGroupingFields != null ) 484 buffer.append( getOutGroupingFields().printVerbose() ).append( "\n" ); 485 486 buffer.append( getOutValuesFields().printVerbose() ); 487 488 return buffer.toString(); 489 } 490 }