    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      }