/******************************************************************************* * Copyright (c) 2009-2013 CWI * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI * * Mark Hills - Mark.Hills@cwi.nl (CWI) * * Arnold Lankamp - Arnold.Lankamp@cwi.nl *******************************************************************************/ package org.rascalmpl.interpreter.result; import static org.rascalmpl.interpreter.result.ResultFactory.bool; import static org.rascalmpl.interpreter.result.ResultFactory.makeResult; import java.util.Iterator; import org.rascalmpl.interpreter.IEvaluatorContext; import org.rascalmpl.interpreter.staticErrors.InvalidDateTimeComparison; import org.rascalmpl.interpreter.staticErrors.UndeclaredField; import org.rascalmpl.interpreter.staticErrors.UnexpectedType; import org.rascalmpl.interpreter.staticErrors.UnsupportedOperation; import org.rascalmpl.interpreter.utils.RuntimeExceptionFactory; import org.rascalmpl.value.IBool; import org.rascalmpl.value.IDateTime; import org.rascalmpl.value.IInteger; import org.rascalmpl.value.IValue; import org.rascalmpl.value.IValueFactory; import org.rascalmpl.value.exceptions.InvalidDateTimeException; import org.rascalmpl.value.type.Type; import org.rascalmpl.value.type.TypeFactory; import org.rascalmpl.value.type.TypeStore; import org.rascalmpl.values.ValueFactoryFactory; import com.ibm.icu.util.Calendar; public class DateTimeResult extends ElementResult<IDateTime> { private static TypeFactory TF = TypeFactory.getInstance(); private static IValueFactory VF = ValueFactoryFactory.getValueFactory(); public static final TypeStore TS = new TypeStore(); public static final Type Duration = TF.abstractDataType(TS, "Duration"); public static final Type duration = TF.constructor(TS, Duration, "duration", TF.integerType(), "years", TF.integerType(), "months", TF.integerType(), "days", TF.integerType(), "hours", TF.integerType(), "minutes", TF.integerType(), "seconds", TF.integerType(), "milliseconds"); public DateTimeResult(Type type, IDateTime value, IEvaluatorContext ctx) { super(type, value, ctx); } public DateTimeResult(Type type, IDateTime value, Iterator<Result<IValue>> iter, IEvaluatorContext ctx) { super(type, value, iter, ctx); } @Override public <V extends IValue> Result<IBool> equals(Result<V> that) { return that.equalToDateTime(this); } @Override protected Result<IBool> equalToDateTime(DateTimeResult that) { checkDateTimeComparison(that); return bool(that.value.getInstant() == this.value.getInstant(), ctx); } @Override public <V extends IValue> Result<IBool> nonEquals(Result<V> that) { return that.nonEqualToDateTime(this); } @Override protected Result<IBool> nonEqualToDateTime(DateTimeResult that) { checkDateTimeComparison(that); return bool(that.value.getInstant() != this.value.getInstant(), ctx); } @Override public <U extends IValue> Result<U> fieldAccess(String name, TypeStore store) { IValueFactory vf = getValueFactory(); IDateTime dt = getValue(); try { if (name.equals("year")) { if (!dt.isTime()) { return makeResult(getTypeFactory().integerType(), vf.integer(dt.getYear()), ctx); } throw new UnsupportedOperation("Can not retrieve the year on a time value",ctx.getCurrentAST()); } else if (name.equals("month")) { if (!dt.isTime()) { return makeResult(getTypeFactory().integerType(), vf.integer(getValue().getMonthOfYear()), ctx); } throw new UnsupportedOperation("Can not retrieve the month on a time value",ctx.getCurrentAST()); } else if (name.equals("day")) { if (!dt.isTime()) { return makeResult(getTypeFactory().integerType(), vf.integer(getValue().getDayOfMonth()), ctx); } throw new UnsupportedOperation("Can not retrieve the day on a time value",ctx.getCurrentAST()); } else if (name.equals("hour")) { if (!dt.isDate()) { return makeResult(getTypeFactory().integerType(), vf.integer(getValue().getHourOfDay()), ctx); } throw new UnsupportedOperation("Can not retrieve the hour on a date value",ctx.getCurrentAST()); } else if (name.equals("minute")) { if (!dt.isDate()) { return makeResult(getTypeFactory().integerType(), vf.integer(getValue().getMinuteOfHour()), ctx); } throw new UnsupportedOperation("Can not retrieve the minute on a date value",ctx.getCurrentAST()); } else if (name.equals("second")) { if (!dt.isDate()) { return makeResult(getTypeFactory().integerType(), vf.integer(getValue().getSecondOfMinute()), ctx); } throw new UnsupportedOperation("Can not retrieve the second on a date value",ctx.getCurrentAST()); } else if (name.equals("millisecond")) { if (!dt.isDate()) { return makeResult(getTypeFactory().integerType(), vf.integer(getValue().getMillisecondsOfSecond()), ctx); } throw new UnsupportedOperation("Can not retrieve the millisecond on a date value",ctx.getCurrentAST()); } else if (name.equals("timezoneOffsetHours")) { if (!dt.isDate()) { return makeResult(getTypeFactory().integerType(), vf.integer(getValue().getTimezoneOffsetHours()), ctx); } throw new UnsupportedOperation("Can not retrieve the timezone offset hours on a date value",ctx.getCurrentAST()); } else if (name.equals("timezoneOffsetMinutes")) { if (!dt.isDate()) { return makeResult(getTypeFactory().integerType(), vf.integer(getValue().getTimezoneOffsetMinutes()), ctx); } throw new UnsupportedOperation("Can not retrieve the timezone offset minutes on a date value",ctx.getCurrentAST()); } else if (name.equals("century")) { if (!dt.isTime()) { return makeResult(getTypeFactory().integerType(), vf.integer(getValue().getCentury()), ctx); } throw new UnsupportedOperation("Can not retrieve the century on a time value",ctx.getCurrentAST()); } else if (name.equals("isDate")) { return makeResult(getTypeFactory().boolType(), vf.bool(getValue().isDate()), ctx); } else if (name.equals("isTime")) { return makeResult(getTypeFactory().boolType(), vf.bool(getValue().isTime()), ctx); } else if (name.equals("isDateTime")) { return makeResult(getTypeFactory().boolType(), vf.bool(getValue().isDateTime()), ctx); } else if (name.equals("justDate")) { if (!dt.isTime()) { return makeResult(getTypeFactory().dateTimeType(), vf.date(dt.getYear(), dt.getMonthOfYear(), dt.getDayOfMonth()), ctx); } throw new UnsupportedOperation("Can not retrieve the date component of a time value",ctx.getCurrentAST()); } else if (name.equals("justTime")) { if (!dt.isDate()) { return makeResult(getTypeFactory().dateTimeType(), vf.time(dt.getHourOfDay(), dt.getMinuteOfHour(), dt.getSecondOfMinute(), dt.getMillisecondsOfSecond(), dt.getTimezoneOffsetHours(), dt.getTimezoneOffsetMinutes()), ctx); } throw new UnsupportedOperation("Can not retrieve the time component of a date value",ctx.getCurrentAST()); } } catch (InvalidDateTimeException e) { throw RuntimeExceptionFactory.illegalArgument(dt, ctx.getCurrentAST(), null, e.getMessage()); } throw new UndeclaredField(name, getTypeFactory().dateTimeType(), ctx.getCurrentAST()); } @Override public <U extends IValue, V extends IValue> Result<U> fieldUpdate( String name, Result<V> repl, TypeStore store) { Type replType = repl.getType(); IValue replValue = repl.getValue(); IDateTime dt = getValue(); // Individual fields int year = dt.getYear(); int month = dt.getMonthOfYear(); int day = dt.getDayOfMonth(); int hour = dt.getHourOfDay(); int minute = dt.getMinuteOfHour(); int second = dt.getSecondOfMinute(); int milli = dt.getMillisecondsOfSecond(); int tzOffsetHour = dt.getTimezoneOffsetHours(); int tzOffsetMin = dt.getTimezoneOffsetMinutes(); try { if (name.equals("year")) { if (dt.isTime()) { throw new UnsupportedOperation("Can not update the year on a time value",ctx.getCurrentAST()); } if (!replType.isInteger()) { throw new UnexpectedType(getTypeFactory().integerType(), replType, ctx.getCurrentAST()); } year = ((IInteger) replValue).intValue(); } else if (name.equals("month")) { if (dt.isTime()) { throw new UnsupportedOperation("Can not update the month on a time value",ctx.getCurrentAST()); } if (!replType.isInteger()) { throw new UnexpectedType(getTypeFactory().integerType(), replType, ctx.getCurrentAST()); } month = ((IInteger) replValue).intValue(); } else if (name.equals("day")) { if (dt.isTime()) { throw new UnsupportedOperation("Can not update the day on a time value",ctx.getCurrentAST()); } if (!replType.isInteger()) { throw new UnexpectedType(getTypeFactory().integerType(), replType, ctx.getCurrentAST()); } day = ((IInteger) replValue).intValue(); } else if (name.equals("hour")) { if (dt.isDate()) { throw new UnsupportedOperation("Can not update the hour on a date value",ctx.getCurrentAST()); } if (!replType.isInteger()) { throw new UnexpectedType(getTypeFactory().integerType(), replType, ctx.getCurrentAST()); } hour = ((IInteger) replValue).intValue(); } else if (name.equals("minute")) { if (dt.isDate()) { throw new UnsupportedOperation("Can not update the minute on a date value",ctx.getCurrentAST()); } if (!replType.isInteger()) { throw new UnexpectedType(getTypeFactory().integerType(), replType, ctx.getCurrentAST()); } minute = ((IInteger) replValue).intValue(); } else if (name.equals("second")) { if (dt.isDate()) { throw new UnsupportedOperation("Can not update the second on a date value",ctx.getCurrentAST()); } if (!replType.isInteger()) { throw new UnexpectedType(getTypeFactory().integerType(), replType, ctx.getCurrentAST()); } second = ((IInteger) replValue).intValue(); } else if (name.equals("millisecond")) { if (dt.isDate()) { throw new UnsupportedOperation("Can not update the millisecond on a date value",ctx.getCurrentAST()); } if (!replType.isInteger()) { throw new UnexpectedType(getTypeFactory().integerType(), replType, ctx.getCurrentAST()); } milli = ((IInteger) replValue).intValue(); } else if (name.equals("timezoneOffsetHours")) { if (dt.isDate()) { throw new UnsupportedOperation("Can not update the timezone offset hours on a date value",ctx.getCurrentAST()); } if (!replType.isInteger()) { throw new UnexpectedType(getTypeFactory().integerType(), replType, ctx.getCurrentAST()); } tzOffsetHour = ((IInteger) replValue).intValue(); } else if (name.equals("timezoneOffsetMinutes")) { if (dt.isDate()) { throw new UnsupportedOperation("Can not update the timezone offset minutes on a date value",ctx.getCurrentAST()); } if (!replType.isInteger()) { throw new UnexpectedType(getTypeFactory().integerType(), replType, ctx.getCurrentAST()); } tzOffsetMin = ((IInteger) replValue).intValue(); } else { throw new UndeclaredField(name, getTypeFactory().dateTimeType(), ctx.getCurrentAST()); } IDateTime newdt = null; if (dt.isDate()) { newdt = getValueFactory().date(year, month, day); } else if (dt.isTime()) { newdt = getValueFactory().time(hour, minute, second, milli, tzOffsetHour, tzOffsetMin); } else { newdt = getValueFactory().datetime(year, month, day, hour, minute, second, milli, tzOffsetHour, tzOffsetMin); } return makeResult(getType(), newdt, ctx); } catch (IllegalArgumentException e) { throw RuntimeExceptionFactory.illegalArgument(repl.getValue(), ctx.getCurrentAST(), null, "Cannot update field " + name + ", this would generate an invalid datetime value"); } catch (InvalidDateTimeException e) { throw RuntimeExceptionFactory.illegalArgument(repl.getValue(), ctx.getCurrentAST(), null, e.getMessage()); } } private void checkDateTimeComparison(DateTimeResult that) { if (that.getValue().isDate()) { if (this.getValue().isTime()) { throw new InvalidDateTimeComparison("date","time",ctx.getCurrentAST()); } else if (this.getValue().isDateTime()) { throw new InvalidDateTimeComparison("date","datetime",ctx.getCurrentAST()); } } else if (that.getValue().isTime()) { if (this.getValue().isDate()) { throw new InvalidDateTimeComparison("time","date",ctx.getCurrentAST()); } else if (this.getValue().isDateTime()) { throw new InvalidDateTimeComparison("time","datetime",ctx.getCurrentAST()); } } else { if (this.getValue().isDate()) { throw new InvalidDateTimeComparison("datetime","date",ctx.getCurrentAST()); } else if (this.getValue().isTime()) { throw new InvalidDateTimeComparison("datetime","time",ctx.getCurrentAST()); } } } @Override public <V extends IValue> Result<IBool> greaterThan(Result<V> that) { return that.greaterThanDateTime(this); } @Override protected Result<IBool> greaterThanDateTime(DateTimeResult that) { checkDateTimeComparison(that); return bool(that.value.getInstant() > this.value.getInstant(), ctx); } @Override public <V extends IValue> Result<IBool> greaterThanOrEqual(Result<V> that) { return that.greaterThanOrEqualDateTime(this); } @Override protected Result<IBool> greaterThanOrEqualDateTime(DateTimeResult that) { checkDateTimeComparison(that); return bool(that.value.getInstant() >= this.value.getInstant(), ctx); } @Override public <V extends IValue> Result<IBool> lessThan(Result<V> that) { return that.lessThanDateTime(this); } @Override protected Result<IBool> lessThanDateTime(DateTimeResult that) { checkDateTimeComparison(that); return bool(that.value.getInstant() < this.value.getInstant(), ctx); } @Override public <V extends IValue> LessThanOrEqualResult lessThanOrEqual(Result<V> that) { return that.lessThanOrEqualDateTime(this); } @Override protected LessThanOrEqualResult lessThanOrEqualDateTime(DateTimeResult that) { checkDateTimeComparison(that); return new LessThanOrEqualResult(that.value.getInstant() < this.value.getInstant(), that.value.getInstant() == this.value.getInstant(), ctx); } @Override public <U extends IValue, V extends IValue> Result<U> subtract(Result<V> that) { return that.subtractDateTime(this); } @Override protected <U extends IValue> Result<U> subtractDateTime(DateTimeResult that) { IDateTime dStart = this.getValue(); Calendar startCal = Calendar.getInstance(); startCal.setTimeInMillis(dStart.getInstant()); IDateTime dEnd = that.getValue(); Calendar endCal = Calendar.getInstance(); endCal.setTimeInMillis(dEnd.getInstant()); if (dStart.isDate()) { if (dEnd.isDate()) { return makeResult(Duration, VF.constructor(DateTimeResult.duration, VF.integer(startCal.fieldDifference(endCal.getTime(), Calendar.YEAR)), VF.integer(startCal.fieldDifference(endCal.getTime(), Calendar.MONTH)), VF.integer(startCal.fieldDifference(endCal.getTime(), Calendar.DAY_OF_MONTH)), VF.integer(0), VF.integer(0), VF.integer(0), VF.integer(0)), ctx); } else if (dEnd.isTime()) { throw RuntimeExceptionFactory.invalidUseOfTimeException("Cannot determine the duration between a date with no time and a time with no date.", ctx.getCurrentAST(), null); } else { throw RuntimeExceptionFactory.invalidUseOfDateTimeException("Cannot determine the duration between a date with no time and a datetime.", ctx.getCurrentAST(), null); } } else if (dStart.isTime()) { if (dEnd.isTime()) { return makeResult(Duration, VF.constructor(DateTimeResult.duration, VF.integer(0), VF.integer(0), VF.integer(0), VF.integer(startCal.fieldDifference(endCal.getTime(), Calendar.HOUR_OF_DAY)), VF.integer(startCal.fieldDifference(endCal.getTime(), Calendar.MINUTE)), VF.integer(startCal.fieldDifference(endCal.getTime(), Calendar.SECOND)), VF.integer(startCal.fieldDifference(endCal.getTime(), Calendar.MILLISECOND))), ctx); } else if (dEnd.isDate()) { throw RuntimeExceptionFactory.invalidUseOfDateException("Cannot determine the duration between a time with no date and a date with no time.", ctx.getCurrentAST(), null); } else { throw RuntimeExceptionFactory.invalidUseOfDateTimeException("Cannot determine the duration between a time with no date and a datetime.", ctx.getCurrentAST(), null); } } else { if (dEnd.isDateTime()) { return makeResult(Duration, VF.constructor(DateTimeResult.duration, VF.integer(startCal.fieldDifference(endCal.getTime(), Calendar.YEAR)), VF.integer(startCal.fieldDifference(endCal.getTime(), Calendar.MONTH)), VF.integer(startCal.fieldDifference(endCal.getTime(), Calendar.DAY_OF_MONTH)), VF.integer(startCal.fieldDifference(endCal.getTime(), Calendar.HOUR_OF_DAY)), VF.integer(startCal.fieldDifference(endCal.getTime(), Calendar.MINUTE)), VF.integer(startCal.fieldDifference(endCal.getTime(), Calendar.SECOND)), VF.integer(startCal.fieldDifference(endCal.getTime(), Calendar.MILLISECOND))), ctx); } else if (dEnd.isDate()) { throw RuntimeExceptionFactory.invalidUseOfDateException("Cannot determine the duration between a datetime and a date with no time.", ctx.getCurrentAST(), null); } else { throw RuntimeExceptionFactory.invalidUseOfTimeException("Cannot determine the duration between a datetime and a time with no date.", ctx.getCurrentAST(), null); } } } @Override protected <U extends IValue> Result<U> addListRelation(ListRelationResult that) { return that.addDateTime(this); } @Override protected <U extends IValue> Result<U> addRelation(RelationResult that) { return that.addDateTime(this); } }