    001/*
    002 * Copyright (c) 2007-2016 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
    021package cascading.tuple.type;
    022
    023import java.lang.reflect.Type;
    024import java.text.ParseException;
    025import java.text.SimpleDateFormat;
    026import java.util.Calendar;
    027import java.util.Date;
    028import java.util.Locale;
    029import java.util.TimeZone;
    030
    031import cascading.CascadingException;
    032import cascading.util.Util;
    033
    034/**
    035 * Class DateCoercibleType is an implementation of {@link CoercibleType}.
    036 * <p/>
    037 * Given a {@code dateFormatString}, using the {@link SimpleDateFormat} format, this CoercibleType
    038 * will convert a value from the formatted string to a {@code Long} canonical type and back.
    039 * <p/>
    040 * This class when presented with a Long timestamp value will assume the value is in UTC.
    041 * <p/>
    042 * See {@link cascading.operation.text.DateParser} and {@link cascading.operation.text.DateFormatter} for similar
    043 * Operations for use within a pipe assembly.
    044 */
    045public class DateType implements CoercibleType<Long>
    046  {
    047  /** Field zone */
    048  protected TimeZone zone;
    049  /** Field locale */
    050  protected Locale locale;
    051  /** Field dateFormatString */
    052  protected String dateFormatString;
    053  /** Field dateFormat */
    054  private transient SimpleDateFormat dateFormat;
    055
    056  /**
    057   * Create a new DateType instance.
    058   *
    059   * @param dateFormatString
    060   * @param zone
    061   * @param locale
    062   */
    063  public DateType( String dateFormatString, TimeZone zone, Locale locale )
    064    {
    065    this.zone = zone;
    066    this.locale = locale;
    067    this.dateFormatString = dateFormatString;
    068    }
    069
    070  public DateType( String dateFormatString, TimeZone zone )
    071    {
    072    this.zone = zone;
    073    this.dateFormatString = dateFormatString;
    074    }
    075
    076  /**
    077   * Create a new DateType instance.
    078   *
    079   * @param dateFormatString
    080   */
    081  public DateType( String dateFormatString )
    082    {
    083    this.dateFormatString = dateFormatString;
    084    }
    085
    086  @Override
    087  public Class getCanonicalType()
    088    {
    089    return Long.TYPE;
    090    }
    091
    092  public SimpleDateFormat getDateFormat()
    093    {
    094    if( dateFormat != null )
    095      return dateFormat;
    096
    097    dateFormat = new SimpleDateFormat( dateFormatString, getLocale() );
    098
    099    dateFormat.setTimeZone( getZone() );
    100
    101    return dateFormat;
    102    }
    103
    104  private Locale getLocale()
    105    {
    106    if( locale != null )
    107      return locale;
    108
    109    return Locale.getDefault();
    110    }
    111
    112  private TimeZone getZone()
    113    {
    114    if( zone != null )
    115      return zone;
    116
    117    return TimeZone.getTimeZone( "UTC" );
    118    }
    119
    120  protected Calendar getCalendar()
    121    {
    122    return Calendar.getInstance( TimeZone.getTimeZone( "UTC" ), getLocale() );
    123    }
    124
    125  @Override
    126  public Long canonical( Object value )
    127    {
    128    if( value == null )
    129      return null;
    130
    131    Class from = value.getClass();
    132
    133    if( from == String.class )
    134      return parse( (String) value ).getTime();
    135
    136    if( from == Date.class )
    137      return ( (Date) value ).getTime(); // in UTC
    138
    139    if( from == Long.class || from == long.class )
    140      return (Long) value;
    141
    142    throw new CascadingException( "unknown type coercion requested from: " + Util.getTypeName( from ) );
    143    }
    144
    145  @Override
    146  public Object coerce( Object value, Type to )
    147    {
    148    if( value == null )
    149      return null;
    150
    151    Class from = value.getClass();
    152
    153    if( from != Long.class )
    154      throw new IllegalStateException( "was not normalized" );
    155
    156    // no coercion, or already in canonical form
    157    if( to == Long.class || to == long.class || to == Object.class )
    158      return value;
    159
    160    if( to == String.class )
    161      {
    162      Calendar calendar = getCalendar();
    163
    164      calendar.setTimeInMillis( (Long) value );
    165
    166      return getDateFormat().format( calendar.getTime() );
    167      }
    168
    169    throw new CascadingException( "unknown type coercion requested, from: " + Util.getTypeName( from ) + " to: " + Util.getTypeName( to ) );
    170    }
    171
    172  private Date parse( String value )
    173    {
    174    try
    175      {
    176      return getDateFormat().parse( value );
    177      }
    178    catch( ParseException exception )
    179      {
    180      throw new CascadingException( "unable to parse value: " + value + " with format: " + dateFormatString );
    181      }
    182    }
    183
    184  @Override
    185  public String toString()
    186    {
    187    final StringBuilder sb = new StringBuilder( "DateType{" );
    188    sb.append( "dateFormatString='" ).append( dateFormatString ).append( '\'' );
    189    sb.append( "," );
    190    sb.append( "canonicalType='" ).append( getCanonicalType() ).append( '\'' );
    191    sb.append( '}' );
    192    return sb.toString();
    193    }
    194  }