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      }