/* * Copyright (c) 2007-2012 Concurrent, Inc. All Rights Reserved. * * Project and contact information: http://www.cascading.org/ * * This file is part of the Cascading project. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package cascading.tuple; import java.beans.ConstructorProperties; /** * Class TupleEntry allows a {@link Tuple} instance and its declarating {@link Fields} instance to be used as a single object. * <p/> * Once a TupleEntry is created, its Fields cannot be changed, but the Tuple instance it holds can be replaced or * modified. The managed Tuple should not have elements added or removed, as this will break the relationship with * the associated Fields instance. * * @see Fields * @see Tuple */ public class TupleEntry { /** Field fields */ Fields fields; /** Field isUnmodifiable */ private boolean isUnmodifiable = false; /** Field tuple */ Tuple tuple; /** * Method select will select a new Tuple instance from the given set of entries. Entries order is significant to * the selector. * * @param selector of type Fields * @param entries of type TupleEntry * @return Tuple */ public static Tuple select( Fields selector, TupleEntry... entries ) { // todo: consider just appending tuples values and just peeking those values Tuple result = null; // does not do field checks if( selector.isAll() ) { for( TupleEntry entry : entries ) { if( result == null ) result = entry.getTuple(); else result = result.append( entry.getTuple() ); } return result; } int size = 0; for( TupleEntry entry : entries ) size += entry.size(); result = Tuple.size( selector.size() ); int offset = 0; for( TupleEntry entry : entries ) { for( int i = 0; i < selector.size(); i++ ) { Comparable field = selector.get( i ); int pos = 0; if( field instanceof String ) { pos = entry.fields.indexOfSafe( field ); if( pos == -1 ) continue; } else { pos = entry.fields.translatePos( (Integer) field, size ) - offset; if( pos >= entry.size() || pos < 0 ) continue; } result.set( i, entry.getObject( pos ) ); // last in wins } offset += entry.size(); } return result; } /** Constructor TupleEntry creates a new TupleEntry instance. */ public TupleEntry() { setFields( new Fields() ); } /** * Constructor TupleEntry creates a new TupleEntry instance. * * @param isUnmodifiable of type boolean */ @ConstructorProperties({"isUnmodifiable"}) public TupleEntry( boolean isUnmodifiable ) { setFields( new Fields() ); this.isUnmodifiable = isUnmodifiable; } /** * Constructor TupleEntry creates a new TupleEntry instance. * * @param fields of type Fields */ @ConstructorProperties({"fields"}) public TupleEntry( Fields fields ) { setFields( fields ); } /** * Constructor TupleEntry creates a new TupleEntry instance. * * @param fields of type Fields * @param isUnmodifiable of type boolean */ @ConstructorProperties({"fields", "isUnmodifiable"}) public TupleEntry( Fields fields, boolean isUnmodifiable ) { setFields( fields ); this.isUnmodifiable = isUnmodifiable; } /** * Constructor TupleEntry creates a new TupleEntry instance. * * @param fields of type Fields * @param tuple of type Tuple */ @ConstructorProperties({"fields", "tuple"}) public TupleEntry( Fields fields, Tuple tuple ) { setFields( fields ); setTuple( tuple ); } /** * Constructor TupleEntry creates a new TupleEntry instance that is a safe copy of the given tupleEntry. * * @param tupleEntry of type TupleEntry */ @ConstructorProperties({"tupleEntry"}) public TupleEntry( TupleEntry tupleEntry ) { setFields( tupleEntry.fields ); setTuple( tupleEntry.getTupleCopy() ); } /** * Constructor TupleEntry creates a new TupleEntry instance. * * @param tuple of type Tuple */ @ConstructorProperties({"tuple"}) public TupleEntry( Tuple tuple ) { setFields( Fields.size( tuple.size() ) ); setTuple( tuple ); } /** * Method isUnmodifiable returns true if this TupleEntry is unmodifiable. * * @return boolean */ public boolean isUnmodifiable() { return isUnmodifiable; } private void setFields( Fields fields ) { if( fields == null ) throw new IllegalArgumentException( "fields many not be null" ); this.fields = fields; } /** * Method getFields returns the fields of this TupleEntry object. * * @return the fields (type Fields) of this TupleEntry object. */ public Fields getFields() { return fields; } /** * Method getTuple returns the tuple of this TupleEntry object. * * @return the tuple (type Tuple) of this TupleEntry object. */ public Tuple getTuple() { return tuple; } /** * Method getTupleCopy returns a copy of the tuple of this TupleEntry object. * * @return a copy of the tuple (type Tuple) of this TupleEntry object. */ public Tuple getTupleCopy() { return new Tuple( tuple ); } /** * Method setTuple sets the tuple of this TupleEntry object. * * @param tuple the tuple of this TupleEntry object. */ public void setTuple( Tuple tuple ) { if( tuple == null ) throw new IllegalArgumentException( "tuple may not be null" ); if( isUnmodifiable ) this.tuple = Tuples.asUnmodifiable( tuple ); else this.tuple = tuple; } /** * Method size returns the number of values in this instance. * * @return int */ public int size() { return tuple.size(); } /** * Method get returns the value in the given position pos. * * @param pos position of the element to return. * @return Comparable */ public Comparable get( int pos ) { return tuple.get( pos ); } /** * Method get returns the value in the given field or position. * <br/> * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will * be considered. * * @param fieldName field name or position to return * @return Comparable */ public Comparable get( Comparable fieldName ) { return tuple.get( fields.getPos( asFieldName( fieldName ) ) ); } /** * Method get returns the value in the given field or position. * <br/> * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will * be considered. * * @param fieldName field name or position to return * @return Comparable */ public Object getObject( Comparable fieldName ) { return tuple.getObject( fields.getPos( asFieldName( fieldName ) ) ); } /** * Method set sets the value in the given field or position. * * @param fieldName field name or position to set * @param value of type Comparable */ public void set( Comparable fieldName, Object value ) { tuple.set( fields.getPos( asFieldName( fieldName ) ), value ); } /** * Method getString returns the element for the given field name or position as a String. * <br/> * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will * be considered. * * @param fieldName field name or position to return * @return String */ public String getString( Comparable fieldName ) { return tuple.getString( fields.getPos( asFieldName( fieldName ) ) ); } /** * Method getFloat returns the element for the given field name or position as a float. Zero if null. * <br/> * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will * be considered. * * @param fieldName field name or position to return * @return float */ public float getFloat( Comparable fieldName ) { return tuple.getFloat( fields.getPos( asFieldName( fieldName ) ) ); } /** * Method getDouble returns the element for the given field name or position as a double. Zero if null. * <br/> * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will * be considered. * * @param fieldName field name or position to return * @return double */ public double getDouble( Comparable fieldName ) { return tuple.getDouble( fields.getPos( asFieldName( fieldName ) ) ); } /** * Method getInteger returns the element for the given field name or position as an int. Zero if null. * <br/> * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will * be considered. * * @param fieldName field name or position to return * @return int */ public int getInteger( Comparable fieldName ) { return tuple.getInteger( fields.getPos( asFieldName( fieldName ) ) ); } /** * Method getLong returns the element for the given field name or position as a long. Zero if null. * <br/> * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will * be considered. * * @param fieldName field name or position to return * @return long */ public long getLong( Comparable fieldName ) { return tuple.getLong( fields.getPos( asFieldName( fieldName ) ) ); } /** * Method getShort returns the element for the given field name or position as a short. Zero if null. * <br/> * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will * be considered. * * @param fieldName field name or position to return * @return short */ public short getShort( Comparable fieldName ) { return tuple.getShort( fields.getPos( asFieldName( fieldName ) ) ); } /** * Method getBoolean returns the element for the given field name or position as a boolean. * If the value is (case ignored) the string 'true', a {@code true} value will be returned. {@code false} if null. * <br/> * {@code fieldName} may optionally be a {@link Fields} instance. Only the first field name or position will * be considered. * * @param fieldName field name or position to return * @return boolean */ public boolean getBoolean( Comparable fieldName ) { return tuple.getBoolean( fields.getPos( asFieldName( fieldName ) ) ); } private Comparable asFieldName( Comparable fieldName ) { if( fieldName instanceof Fields ) { Fields fields = (Fields) fieldName; if( !fields.isDefined() ) throw new TupleException( "given Fields instance must explicitly declare one field name or position: " + fields.printVerbose() ); fieldName = fields.get( 0 ); } return fieldName; } /** * Method selectEntry selects the fields specified in selector from this instance. * * @param selector Fields selector that selects the values to return * @return TupleEntry */ public TupleEntry selectEntry( Fields selector ) { if( selector == null || selector.isAll() ) return this; try { return new TupleEntry( Fields.asDeclaration( selector ), tuple.get( this.fields, selector ) ); } catch( Exception exception ) { throw new TupleException( "unable to select from: " + this.fields.print() + ", using selector: " + selector.print(), exception ); } } /** * Method selectTuple selects the fields specified in selector from this instance. * * @param selector Fields selector that selects the values to return * @return Tuple */ public Tuple selectTuple( Fields selector ) { if( selector == null || selector.isAll() ) return this.tuple; try { return tuple.get( fields, selector ); } catch( Exception exception ) { throw new TupleException( "unable to select from: " + this.fields.print() + ", using selector: " + selector.print(), exception ); } } /** * Method selectInteger selects the first field Tuple value in the specified selector. * <br/> * All other fields in the selector are ignored. * * @param selector * @return an int value */ @Deprecated public int selectInteger( Fields selector ) { if( !selector.isDefined() ) throw new TupleException( "given selector must define a field name or position to select with" ); return tuple.getInteger( fields.getPos( selector.get( 0 ) ) ); } /** * Method setTuple sets the values specified by the selector to the values given by the given tuple. * * @param selector of type Fields * @param tuple of type Tuple */ public void setTuple( Fields selector, Tuple tuple ) { if( selector == null || selector.isAll() ) { this.tuple = tuple; return; } try { this.tuple.set( fields, selector, tuple ); } catch( Exception exception ) { throw new TupleException( "unable to select from: " + this.fields.print() + ", using selector: " + selector.print(), exception ); } } /** * Method set sets the values from the given tupleEntry into this TupleEntry instance based on the given * tupleEntry field names. * * @param tupleEntry of type TupleEntry */ public void set( TupleEntry tupleEntry ) { try { this.tuple.set( fields, tupleEntry.getFields(), tupleEntry.getTuple() ); } catch( Exception exception ) { throw new TupleException( "unable to select from: " + this.fields.print() + ", using selector: " + tupleEntry.getFields().print(), exception ); } } /** * Method appendNew appends the given TupleEntry instance to this instance. * * @param entry of type TupleEntry * @return TupleEntry */ public TupleEntry appendNew( TupleEntry entry ) { TupleEntry result = new TupleEntry(); result.fields = fields.append( entry.fields.isUnknown() ? Fields.size( entry.tuple.size() ) : entry.fields ); result.tuple = tuple.append( entry.tuple ); return result; } @Override public String toString() { if( fields == null ) return "empty"; else if( tuple == null ) return "fields: " + fields.print(); else return "fields: " + fields.print() + " tuple: " + tuple.print(); } }