001/*
002 * Copyright (c) 2016-2017 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.operation;
023
024import java.beans.ConstructorProperties;
025import java.io.PrintStream;
026
027import cascading.flow.FlowProcess;
028
029/**
030 * Class Debug is a {@link Filter} that will never remove an item from a stream, but will print the Tuple to either
031 * stdout or stderr.
032 * <p>
033 * Currently, if printFields is true, they will print every 10 Tuples.
034 * <p>
035 * The frequency that fields and tuples are printed can be set via {@link #setPrintFieldsEvery(int)} and
036 * {@link #setPrintTupleEvery(int)} methods, respectively.
037 */
038@SuppressWarnings({"UseOfSystemOutOrSystemErr"})
039public class Debug extends BaseOperation<Long> implements Filter<Long>, PlannedOperation<Long>
040  {
041  static public enum Output
042    {
043      STDOUT, STDERR
044    }
045
046  /** Field output */
047  private Output output = Output.STDERR;
048  /** Field prefix */
049  private String prefix = null;
050  /** Field printFields */
051  private boolean printFields = false;
052
053  /** Field printFieldsEvery */
054  private int printFieldsEvery = 10;
055  /** Field printTupleEvery */
056  private int printTupleEvery = 1;
057
058  /**
059   * Constructor Debug creates a new Debug instance that prints to stderr by default, and does not print
060   * the Tuple instance field names.
061   */
062  public Debug()
063    {
064    }
065
066  /**
067   * Constructor Debug creates a new Debug instance that prints to stderr by default, and does not print
068   * the Tuple instance field names.
069   *
070   * @param prefix of type String
071   */
072  @ConstructorProperties({"prefix"})
073  public Debug( String prefix )
074    {
075    this.prefix = prefix;
076    }
077
078  /**
079   * Constructor Debug creates a new Debug instance that prints to stderr and will print the current
080   * Tuple instance field names if printFields is true.
081   *
082   * @param prefix      of type String
083   * @param printFields of type boolean
084   */
085  @ConstructorProperties({"prefix", "printFields"})
086  public Debug( String prefix, boolean printFields )
087    {
088    this.prefix = prefix;
089    this.printFields = printFields;
090    }
091
092  /**
093   * Constructor Debug creates a new Debug instance that prints to stderr and will print the current
094   * Tuple instance field names if printFields is true.
095   *
096   * @param printFields of type boolean
097   */
098  @ConstructorProperties({"printFields"})
099  public Debug( boolean printFields )
100    {
101    this.printFields = printFields;
102    }
103
104  /**
105   * Constructor Debug creates a new Debug instance that prints to the declared stream and does not print the Tuple
106   * field names.
107   *
108   * @param output of type Output
109   */
110  @ConstructorProperties({"output"})
111  public Debug( Output output )
112    {
113    this.output = output;
114    }
115
116  /**
117   * Constructor Debug creates a new Debug instance that prints to the declared stream and does not print the Tuple
118   * field names.
119   *
120   * @param output of type Output
121   * @param prefix of type String
122   */
123  @ConstructorProperties({"output", "prefix"})
124  public Debug( Output output, String prefix )
125    {
126    this.output = output;
127    this.prefix = prefix;
128    }
129
130  /**
131   * Constructor Debug creates a new Debug instance that prints to the declared stream and will print the Tuple instances
132   * field names if printFields is true.
133   *
134   * @param output      of type Output
135   * @param prefix      of type String
136   * @param printFields of type boolean
137   */
138  @ConstructorProperties({"output", "prefix", "printFields"})
139  public Debug( Output output, String prefix, boolean printFields )
140    {
141    this.output = output;
142    this.prefix = prefix;
143    this.printFields = printFields;
144    }
145
146  /**
147   * Constructor Debug creates a new Debug instance that prints to the declared stream and will print the Tuple instances
148   * field names if printFields is true.
149   *
150   * @param output      of type Output
151   * @param printFields of type boolean
152   */
153  @ConstructorProperties({"output", "printFields"})
154  public Debug( Output output, boolean printFields )
155    {
156    this.output = output;
157    this.printFields = printFields;
158    }
159
160  public Output getOutput()
161    {
162    return output;
163    }
164
165  public String getPrefix()
166    {
167    return prefix;
168    }
169
170  public boolean isPrintFields()
171    {
172    return printFields;
173    }
174
175  /**
176   * Method getPrintFieldsEvery returns the printFieldsEvery interval value of this Debug object.
177   *
178   * @return the printFieldsEvery (type int) of this Debug object.
179   */
180  public int getPrintFieldsEvery()
181    {
182    return printFieldsEvery;
183    }
184
185  /**
186   * Method setPrintFieldsEvery sets the printFieldsEvery interval value of this Debug object.
187   *
188   * @param printFieldsEvery the printFieldsEvery of this Debug object.
189   */
190  public void setPrintFieldsEvery( int printFieldsEvery )
191    {
192    this.printFieldsEvery = printFieldsEvery;
193    }
194
195  /**
196   * Method getPrintTupleEvery returns the printTupleEvery interval value of this Debug object.
197   *
198   * @return the printTupleEvery (type int) of this Debug object.
199   */
200  public int getPrintTupleEvery()
201    {
202    return printTupleEvery;
203    }
204
205  /**
206   * Method setPrintTupleEvery sets the printTupleEvery interval value of this Debug object.
207   *
208   * @param printTupleEvery the printTupleEvery of this Debug object.
209   */
210  public void setPrintTupleEvery( int printTupleEvery )
211    {
212    this.printTupleEvery = printTupleEvery;
213    }
214
215  @Override
216  public boolean supportsPlannerLevel( PlannerLevel plannerLevel )
217    {
218    return plannerLevel instanceof DebugLevel;
219    }
220
221  @Override
222  public void prepare( FlowProcess flowProcess, OperationCall<Long> operationCall )
223    {
224    super.prepare( flowProcess, operationCall );
225
226    operationCall.setContext( 0L );
227    }
228
229  /** @see Filter#isRemove(cascading.flow.FlowProcess, FilterCall) */
230  public boolean isRemove( FlowProcess flowProcess, FilterCall<Long> filterCall )
231    {
232    PrintStream stream = output == Output.STDOUT ? System.out : System.err;
233
234    if( printFields && filterCall.getContext() % printFieldsEvery == 0 )
235      print( stream, filterCall.getArguments().getFields().print() );
236
237    if( filterCall.getContext() % printTupleEvery == 0 )
238      print( stream, filterCall.getArguments().getTuple().print() );
239
240    filterCall.setContext( filterCall.getContext() + 1 );
241
242    return false;
243    }
244
245  @Override
246  public void cleanup( FlowProcess flowProcess, OperationCall<Long> longOperationCall )
247    {
248    if( longOperationCall.getContext() == null )
249      return;
250
251    PrintStream stream = output == Output.STDOUT ? System.out : System.err;
252
253    print( stream, "tuples count: " + longOperationCall.getContext().toString() );
254    }
255
256  private void print( PrintStream stream, String message )
257    {
258    if( prefix != null )
259      {
260      stream.print( prefix );
261      stream.print( ": " );
262      }
263
264    stream.println( message );
265    }
266
267  @Override
268  public boolean equals( Object object )
269    {
270    if( this == object )
271      return true;
272    if( !( object instanceof Debug ) )
273      return false;
274    if( !super.equals( object ) )
275      return false;
276
277    Debug debug = (Debug) object;
278
279    if( printFields != debug.printFields )
280      return false;
281    if( printFieldsEvery != debug.printFieldsEvery )
282      return false;
283    if( printTupleEvery != debug.printTupleEvery )
284      return false;
285    if( output != debug.output )
286      return false;
287    if( prefix != null ? !prefix.equals( debug.prefix ) : debug.prefix != null )
288      return false;
289
290    return true;
291    }
292
293  @Override
294  public int hashCode()
295    {
296    int result = super.hashCode();
297    result = 31 * result + ( output != null ? output.hashCode() : 0 );
298    result = 31 * result + ( prefix != null ? prefix.hashCode() : 0 );
299    result = 31 * result + ( printFields ? 1 : 0 );
300    result = 31 * result + printFieldsEvery;
301    result = 31 * result + printTupleEvery;
302    return result;
303    }
304  }