package org.basex.query.item;
import static org.basex.query.QueryText.*;
import static org.basex.util.Token.*;
import java.math.BigDecimal;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.datatype.Duration;
import org.basex.query.QueryException;
import org.basex.query.util.Err;
import org.basex.util.InputInfo;
import org.basex.util.Token;
import org.basex.util.TokenBuilder;
import org.basex.util.Util;
/**
* Duration item.
*
* @author BaseX Team 2005-12, BSD License
* @author Christian Gruen
*/
public class Dur extends Item {
/** Seconds per day. */
static final long DAYSECONDS = 86400;
/** Date pattern. */
private static final Pattern DUR = Pattern.compile(
"(-?)P(([0-9]+)Y)?(([0-9]+)M)?(([0-9]+)D)?" +
"(T(([0-9]+)H)?(([0-9]+)M)?(([0-9]+(\\.[0-9]+)?)?S)?)?");
/** Number of months. */
int mon;
/** Number of seconds. */
BigDecimal sc;
/**
* Constructor.
* @param v value
* @param ii input info
* @throws QueryException query exception
*/
public Dur(final byte[] v, final InputInfo ii) throws QueryException {
this(v, AtomType.DUR, ii);
}
/**
* Constructor.
* @param t data type
*/
Dur(final Type t) {
super(t);
}
/**
* Constructor.
* @param d duration
*/
Dur(final Dur d) {
this(d, AtomType.DUR);
}
/**
* Constructor.
* @param d duration
* @param t data type
*/
private Dur(final Dur d, final Type t) {
this(t);
mon = d.mon;
sc = d.sc == null ? BigDecimal.valueOf(0) : d.sc;
}
/**
* Constructor.
* @param v value
* @param t data type
* @param ii input info
* @throws QueryException query exception
*/
private Dur(final byte[] v, final Type t, final InputInfo ii)
throws QueryException {
this(t);
final String val = Token.string(v).trim();
final Matcher mt = DUR.matcher(val);
if(!mt.matches() || val.endsWith("P") || val.endsWith("T"))
dateErr(v, XDURR, ii);
final int y = mt.group(2) != null ? toInt(mt.group(3)) : 0;
final int m = mt.group(4) != null ? toInt(mt.group(5)) : 0;
final long d = mt.group(6) != null ? toInt(mt.group(7)) : 0;
final long h = mt.group(9) != null ? toInt(mt.group(10)) : 0;
final long n = mt.group(11) != null ? toInt(mt.group(12)) : 0;
final double s = mt.group(13) != null ? toDouble(token(mt.group(14))) : 0;
mon = y * 12 + m;
sc = BigDecimal.valueOf(d * DAYSECONDS + h * 3600 + n * 60);
sc = sc.add(BigDecimal.valueOf(s));
if(!mt.group(1).isEmpty()) {
mon = -mon;
sc = sc.negate();
}
}
/**
* Returns the years.
* @return year
*/
public final int yea() {
return mon / 12;
}
/**
* Returns the months.
* @return year
*/
public final int mon() {
return mon % 12;
}
/**
* Returns the days.
* @return day
*/
public final long day() {
return sc.longValue() / DAYSECONDS;
}
/**
* Returns the time.
* @return time
*/
private long tim() {
return sc.longValue() % DAYSECONDS;
}
/**
* Returns the hours.
* @return day
*/
public final long hou() {
return tim() / 3600;
}
/**
* Returns the minutes.
* @return day
*/
public final long min() {
return tim() % 3600 / 60;
}
/**
* Returns the seconds.
* @return day
*/
public final BigDecimal sec() {
return sc.remainder(BigDecimal.valueOf(60));
}
@Override
public byte[] string(final InputInfo ii) {
final TokenBuilder tb = new TokenBuilder();
if(mon < 0 || sc.signum() < 0) tb.add('-');
tb.add('P');
if(yea() != 0) { tb.addLong(Math.abs(yea())); tb.add('Y'); }
if(mon() != 0) { tb.addLong(Math.abs(mon())); tb.add('M'); }
if(day() != 0) { tb.addLong(Math.abs(day())); tb.add('D'); }
if(sc.remainder(BigDecimal.valueOf(DAYSECONDS)).signum() != 0) {
tb.add('T');
if(hou() != 0) { tb.addLong(Math.abs(hou())); tb.add('H'); }
if(min() != 0) { tb.addLong(Math.abs(min())); tb.add('M'); }
if(sec().signum() != 0) { tb.add(sc()); tb.add('S'); }
}
if(mon == 0 && sc.signum() == 0) tb.add("T0S");
return tb.finish();
}
/**
* Returns the seconds in a token.
* @return seconds
*/
final byte[] sc() {
return chopNumber(token(sec().abs().toPlainString()));
}
@Override
public final boolean eq(final InputInfo ii, final Item it)
throws QueryException {
final Dur d = (Dur) (!it.type.isDuration() ? type.cast(it, null, ii) : it);
final double s1 = sc == null ? 0 : sc.doubleValue();
final double s2 = d.sc == null ? 0 : d.sc.doubleValue();
return mon == d.mon && s1 == s2;
}
@Override
public int diff(final InputInfo ii, final Item it) throws QueryException {
throw Err.diff(ii, it, this);
}
@Override
public final Duration toJava() {
return Date.df.newDuration(Token.string(string(null)));
}
@Override
public final int hash(final InputInfo ii) {
return (int) (31 * mon + (sc == null ? 0 : sc.doubleValue()));
}
@Override
public final String toString() {
return Util.info("\"%\"", string(null));
}
}