<filename>src/org/basex/query/item/AtomType.java<gh_stars>0
package org.basex.query.item;

import org.basex.query.QueryContext;
import org.basex.query.QueryException;
import static org.basex.query.QueryText.*;
import org.basex.query.util.Err;
import static org.basex.query.util.Err.*;
import org.basex.util.*;
import static org.basex.util.Token.*;

import javax.xml.namespace.QName;
import java.math.BigDecimal;
import java.util.regex.Pattern;

/**
 * XQuery data types.
 *
 * @author BaseX Team 2005-12, BSD License
 * @author <NAME>
 */
public enum AtomType implements Type {
  /** Item type. */
  ITEM("item", null, EMPTY, false, false, false, false, false),

  /** Any simple type. */
  UTY("untyped", null, EMPTY, false, false, false, false, false),

  /** Any simple type. */
  ATY("anyType", null, EMPTY, false, false, false, false, false),

  /** Any simple type. */
  AST("anySimpleType", null, EMPTY, false, false, false, false, false),

  /** Any atomic type. */
  AAT("anyAtomicType", ITEM, XSURI, false, false, false, false, false) {
    @Override
    public Atm cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return new Atm(it.string(ii));
    }
    @Override
    public Atm cast(final Object o, final InputInfo ii) {
      return new Atm(token(o.toString()));
    }
  },

  /** Untyped Atomic type. */
  ATM("untypedAtomic", AAT, XSURI, false, true, true, false, false) {
    @Override
    public Atm cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return new Atm(it.string(ii));
    }
    @Override
    public Atm cast(final Object o, final InputInfo ii) {
      return new Atm(token(o.toString()));
    }
  },

  /** String type. */
  STR("string", AAT, XSURI, false, false, true, false, false) {
    @Override
    public Str cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return Str.get(it.string(ii));
    }
    @Override
    public Str cast(final Object o, final InputInfo ii) {
      return Str.get(o);
    }
  },

  /** Normalized String type. */
  NST("normalizedString", STR, XSURI, false, false, true, false, false) {
    @Override
    public Str cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return new Str(it.string(ii), this);
    }
    @Override
    public Str cast(final Object o, final InputInfo ii) throws QueryException {
      return cast(Str.get(o), null, ii);
    }
  },

  /** Token type. */
  TOK("token", NST, XSURI, false, false, true, false, false) {
    @Override
    public Str cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return new Str(norm(it.string(ii)), this);
    }
    @Override
    public Str cast(final Object o, final InputInfo ii) throws QueryException {
      return cast(Str.get(o), null, ii);
    }
  },

  /** Language type. */
  LAN("language", TOK, XSURI, false, false, true, false, false) {
    @Override
    public Str cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      final byte[] v = norm(it.string(ii));
      if(!LANGPATTERN.matcher(Token.string(v)).matches()) error(it, ii);
      return new Str(v, this);
    }
    @Override
    public Str cast(final Object o, final InputInfo ii) throws QueryException {
      return cast(Str.get(o), null, ii);
    }
  },

  /** NMTOKEN type. */
  NMT("NMTOKEN", TOK, XSURI, false, false, true, false, false) {
    @Override
    public Str cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      final byte[] v = norm(it.string(ii));
      if(!XMLToken.isNMToken(v)) error(it, ii);
      return new Str(v, this);
    }
    @Override
    public Str cast(final Object o, final InputInfo ii) throws QueryException {
      return cast(Str.get(o), null, ii);
    }
  },

  /** Name type. */
  NAM("Name", TOK, XSURI, false, false, true, false, false) {
    @Override
    public Str cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      final byte[] v = norm(it.string(ii));
      if(!XMLToken.isName(v)) error(it, ii);
      return new Str(v, this);
    }
    @Override
    public Str cast(final Object o, final InputInfo ii) throws QueryException {
      return cast(Str.get(o), null, ii);
    }
  },

  /** NCName type. */
  NCN("NCName", NAM, XSURI, false, false, true, false, false) {
    @Override
    public Str cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return new Str(checkName(it, ii), this);
    }
    @Override
    public Str cast(final Object o, final InputInfo ii) throws QueryException {
      return cast(Str.get(o), null, ii);
    }
  },

  /** ID type. */
  ID("ID", NCN, XSURI, false, false, true, false, false) {
    @Override
    public Str cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return new Str(checkName(it, ii), this);
    }
    @Override
    public Str cast(final Object o, final InputInfo ii) throws QueryException {
      return cast(Str.get(o), null, ii);
    }
  },

  /** IDREF type. */
  IDR("IDREF", NCN, XSURI, false, false, true, false, false) {
    @Override
    public Str cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return new Str(checkName(it, ii), this);
    }
    @Override
    public Str cast(final Object o, final InputInfo ii) throws QueryException {
      return cast(Str.get(o), null, ii);
    }
  },

  /** Entity type. */
  ENT("ENTITY", NCN, XSURI, false, false, true, false, false) {
    @Override
    public Str cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return new Str(checkName(it, ii), this);
    }
    @Override
    public Str cast(final Object o, final InputInfo ii) throws QueryException {
      return cast(Str.get(o), null, ii);
    }
  },

  /** Float type. */
  FLT("float", AAT, XSURI, true, false, false, false, false) {
    @Override
    public Flt cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return Flt.get(checkNum(it, ii).flt(ii));
    }
    @Override
    public Flt cast(final Object o, final InputInfo ii) throws QueryException {
      return cast(Str.get(o), null, ii);
    }
  },

  /** Double type. */
  DBL("double", AAT, XSURI, true, false, false, false, false) {
    @Override
    public Dbl cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return Dbl.get(checkNum(it, ii).dbl(ii));
    }
    @Override
    public Dbl cast(final Object o, final InputInfo ii) throws QueryException {
      return cast(Str.get(o), null, ii);
    }
  },

  /** Decimal type. */
  DEC("decimal", AAT, XSURI, true, false, false, false, false) {
    @Override
    public Dec cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return Dec.get(checkNum(it, ii).dec(ii));
    }
    @Override
    public Dec cast(final Object o, final InputInfo ii) {
      return Dec.get(new BigDecimal(o.toString()));
    }
  },

  /** Precision decimal type. */
  PDC("precisionDecimal", null, EMPTY, false, false, false, false, false),

  /** Integer type. */
  ITR("integer", DEC, XSURI, true, false, false, false, false) {
    @Override
    public Int cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return cast(it, ii);
    }
    @Override
    public Int cast(final Object o, final InputInfo ii) throws QueryException {
      return Int.get(checkLong(o, 0, 0, ii));
    }
  },

  /** Non-positive integer type. */
  NPI("nonPositiveInteger", ITR, XSURI, true, false, false, false, false) {
    @Override
    public Int cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return cast(it, ii);
    }
    @Override
    public Int cast(final Object o, final InputInfo ii) throws QueryException {
      return new Int(checkLong(o, Long.MIN_VALUE, 0, ii), this);
    }
  },

  /** Negative integer type. */
  NIN("negativeInteger", NPI, XSURI, true, false, false, false, false) {
    @Override
    public Int cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return cast(it, ii);
    }
    @Override
    public Int cast(final Object o, final InputInfo ii) throws QueryException {
      return new Int(checkLong(o, Long.MIN_VALUE, -1, ii), this);
    }
  },

  /** Long type. */
  LNG("long", ITR, XSURI, true, false, false, false, false) {
    @Override
    public Int cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return it.type.isDate() ? new Int((Date) it) : cast(it, ii);
    }
    @Override
    public Int cast(final Object o, final InputInfo ii) throws QueryException {
      return new Int(checkLong(o, 0, 0, ii), this);
    }
  },

  /** Int type. */
  INT("int", LNG, XSURI, true, false, false, false, false) {
    @Override
    public Int cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return cast(it, ii);
    }
    @Override
    public Int cast(final Object o, final InputInfo ii) throws QueryException {
      return new Int(checkLong(o, -0x80000000, 0x7FFFFFFF, ii), this);
    }
  },

  /** Short type. */
  SHR("short", INT, XSURI, true, false, false, false, false) {
    @Override
    public Int cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return cast(it, ii);
    }
    @Override
    public Int cast(final Object o, final InputInfo ii) throws QueryException {
      return new Int(checkLong(o, -0x8000, 0x7FFF, ii), this);
    }
  },

  /** Byte type. */
  BYT("byte", SHR, XSURI, true, false, false, false, false) {
    @Override
    public Int cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return cast(it, ii);
    }
    @Override
    public Int cast(final Object o, final InputInfo ii) throws QueryException {
      return new Int(checkLong(o, -0x80, 0x7F, ii), this);
    }
  },

  /** Non-negative integer type. */
  NNI("nonNegativeInteger", ITR, XSURI, true, false, false, false, false) {
    @Override
    public Int cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return cast(it, ii);
    }
    @Override
    public Int cast(final Object o, final InputInfo ii) throws QueryException {
      return new Int(checkLong(o, 0, Long.MAX_VALUE, ii), this);
    }
  },

  /** Unsigned long type. */
  ULN("unsignedLong", NNI, XSURI, true, false, false, false, false) {
    /** Maximum value. */
    final BigDecimal max = new BigDecimal(Long.MAX_VALUE).multiply(
        BigDecimal.valueOf(2)).add(BigDecimal.ONE);

    @Override
    public Dec cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      final BigDecimal v = checkNum(it, ii).dec(ii);
      if(v.signum() < 0 || v.compareTo(max) > 0 || it.type.isString() &&
          contains(it.string(ii), '.')) FUNCAST.thrw(ii, this, it);
      return new Dec(v, this);
    }
    @Override
    public Dec cast(final Object o, final InputInfo ii) {
      return new Dec(token(o.toString()));
    }
  },

  /** Short type. */
  UIN("unsignedInt", ULN, XSURI, true, false, false, false, false) {
    @Override
    public Int cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return cast(it, ii);
    }
    @Override
    public Int cast(final Object o, final InputInfo ii) throws QueryException {
      return new Int(checkLong(o, 0, 0xFFFFFFFFL, ii), this);
    }
  },

  /** Unsigned Short type. */
  USH("unsignedShort", UIN, XSURI, true, false, false, false, false) {
    @Override
    public Int cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return cast(it, ii);
    }
    @Override
    public Int cast(final Object o, final InputInfo ii) throws QueryException {
      return new Int(checkLong(o, 0, 0xFFFF, ii), this);
    }
  },

  /** Unsigned byte type. */
  UBY("unsignedByte", USH, XSURI, true, false, false, false, false) {
    @Override
    public Int cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return cast(it, ii);
    }
    @Override
    public Int cast(final Object o, final InputInfo ii) throws QueryException {
      return new Int(checkLong(o, 0, 0xFF, ii), this);
    }
  },

  /** Positive integer type. */
  PIN("positiveInteger", NNI, XSURI, true, false, false, false, false) {
    @Override
    public Int cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return cast(it, ii);
    }
    @Override
    public Int cast(final Object o, final InputInfo ii) throws QueryException {
      return new Int(checkLong(o, 1, Long.MAX_VALUE, ii), this);
    }
  },

  /** Duration type. */
  DUR("duration", AAT, XSURI, false, false, false, true, false) {
    @Override
    public Item cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return it.type.isDuration() ? new Dur((Dur) it) : str(it) ?
          new Dur(it.string(ii), ii) : error(it, ii);
    }
    @Override
    public Item cast(final Object o, final InputInfo ii) throws QueryException {
      return cast(Str.get(o), null, ii);
    }
  },

  /** Year month duration type. */
  YMD("yearMonthDuration", DUR, XSURI, false, false, false, true, false) {
    @Override
    public Item cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return it.type.isDuration() ? new YMd((Dur) it) : str(it) ?
          new YMd(it.string(ii), ii) : error(it, ii);
    }
    @Override
    public Item cast(final Object o, final InputInfo ii) throws QueryException {
      return cast(Str.get(o), null, ii);
    }
  },

  /** Day time duration type. */
  DTD("dayTimeDuration", DUR, XSURI, false, false, false, true, false) {
    @Override
    public Item cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return it.type.isDuration() ? new DTd((Dur) it) : str(it) ?
          new DTd(it.string(ii), ii) : error(it, ii);
    }
    @Override
    public Item cast(final Object o, final InputInfo ii) throws QueryException {
      return cast(Str.get(o), null, ii);
    }
  },

  /** DateTime type. */
  DTM("dateTime", AAT, XSURI, false, false, false, false, true) {
    @Override
    public Item cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return it.type == LNG ? new Dtm((Int) it, ii) : it.type == DAT ?
          new Dtm((Date) it) : str(it) ? new Dtm(it.string(ii), ii) :
            error(it, ii);
    }
    @Override
    public Item cast(final Object o, final InputInfo ii) throws QueryException {
      return cast(Str.get(o), null, ii);
    }
  },

  /** DateTimeStamp type. */
  DTS("dateTimeStamp", null, EMPTY, false, false, false, false, true),

  /** Date type. */
  DAT("date", AAT, XSURI, false, false, false, false, true) {
    @Override
    public Item cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return it.type == DTM ? new Dat((Date) it) : str(it) ?
          new Dat(it.string(ii), ii) : error(it, ii);
    }
    @Override
    public Item cast(final Object o, final InputInfo ii) throws QueryException {
      return cast(Str.get(o), null, ii);
    }
  },

  /** Time type. */
  TIM("time", AAT, XSURI, false, false, false, false, true) {
    @Override
    public Item cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return it.type == DTM ? new Tim((Date) it) : str(it) ?
          new Tim(it.string(ii), ii) : error(it, ii);
    }
    @Override
    public Item cast(final Object o, final InputInfo ii) throws QueryException {
      return cast(Str.get(o), null, ii);
    }
  },

  /** Year month type. */
  YMO("gYearMonth", AAT, XSURI, false, false, false, false, true) {
    @Override
    public Item cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return it.type == DTM || it.type == DAT ?
          new DSim((Date) it, this) : str(it) ?
          new DSim(it.string(ii), this, ii) : error(it, ii);
    }
    @Override
    public Item cast(final Object o, final InputInfo ii) throws QueryException {
      return cast(Str.get(o), null, ii);
    }
  },

  /** Year type. */
  YEA("gYear", AAT, XSURI, false, false, false, false, true) {
    @Override
    public Item cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return it.type == DTM || it.type == DAT ?
          new DSim((Date) it, this) : str(it) ?
          new DSim(it.string(ii), this, ii) : error(it, ii);
    }
    @Override
    public Item cast(final Object o, final InputInfo ii) throws QueryException {
      return cast(Str.get(o), null, ii);
    }
  },

  /** Month day type. */
  MDA("gMonthDay", AAT, XSURI, false, false, false, false, true) {
    @Override
    public Item cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return it.type == DTM || it.type == DAT ?
          new DSim((Date) it, this) : str(it) ?
          new DSim(it.string(ii), this, ii) : error(it, ii);
    }
    @Override
    public Item cast(final Object o, final InputInfo ii) throws QueryException {
      return cast(Str.get(o), null, ii);
    }
  },

  /** Day type. */
  DAY("gDay", AAT, XSURI, false, false, false, false, true) {
    @Override
    public Item cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return it.type == DTM || it.type == DAT ?
          new DSim((Date) it, this) : str(it) ?
          new DSim(it.string(ii), this, ii) : error(it, ii);
    }
    @Override
    public Item cast(final Object o, final InputInfo ii) throws QueryException {
      return cast(Str.get(o), null, ii);
    }
  },

  /** Month type. */
  MON("gMonth", AAT, XSURI, false, false, false, false, true) {
    @Override
    public Item cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return it.type == DTM || it.type == DAT ?
          new DSim((Date) it, this) : str(it) ?
          new DSim(it.string(ii), this, ii) : error(it, ii);
    }
    @Override
    public Item cast(final Object o, final InputInfo ii) throws QueryException {
      return cast(Str.get(o), null, ii);
    }
  },

  /** Boolean type. */
  BLN("boolean", AAT, XSURI, false, false, false, false, false) {
    @Override
    public Item cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return it.type.isNumber() ? Bln.get(it.bool(ii)) : str(it) ?
          Bln.get(Bln.parse(it.string(ii), ii)) : error(it, ii);
    }
    @Override
    public Item cast(final Object o, final InputInfo ii) {
      return Bln.get((Boolean) o);
    }
  },

  /** Implementation specific: binary type. */
  BIN("binary", AAT, BASEXURI, false, false, false, false, false),

  /** Base64 binary type. */
  B64("base64Binary", BIN, XSURI, false, false, false, false, false) {
    @Override
    public Item cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return it instanceof Bin ? new B64((Bin) it, ii) : str(it) ?
          new B64(it.string(ii), ii) : error(it, ii);
    }
    @Override
    public Item cast(final Object o, final InputInfo ii) throws QueryException {
      return new B64((byte[]) o, ii);
    }
  },

  /** Hex binary type. */
  HEX("hexBinary", BIN, XSURI, false, false, false, false, false) {
    @Override
    public Item cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {
      return it instanceof Bin ? new Hex((Bin) it, ii) : str(it) ?
          new Hex(it.string(ii), ii) : error(it, ii);
    }
    @Override
    public Item cast(final Object o, final InputInfo ii) throws QueryException {
      return new Hex((byte[]) o, ii);
    }
  },

  /** Any URI type. */
  URI("anyURI", AAT, XSURI, false, false, true, false, false) {
    @Override
    public Item cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {

      if(!it.type.isString()) error(it, ii);
      final Uri u = Uri.uri(it.string(ii));
      if(!u.isValid()) FUNCAST.thrw(ii, this, it);
      return u;
    }
    @Override
    public Item cast(final Object o, final InputInfo ii) {
      return Uri.uri(token(o.toString()));
    }
  },

  /** QName Type. */
  QNM("QName", AAT, XSURI, false, false, false, false, false) {
    @Override
    public Item cast(final Item it, final QueryContext ctx, final InputInfo ii)
        throws QueryException {

      // argument must be of type string and a valid QName
      if(it.type != STR) error(it, ii);
      final byte[] nm = it.string(ii);
      if(nm.length == 0) FUNCAST.thrw(ii, this, it);
      final QNm name = new QNm(nm, ctx);
      if(!name.hasURI() && name.hasPrefix()) NSDECL.thrw(ii, name.prefix());
      return name;
    }
    @Override
    public Item cast(final Object o, final InputInfo ii) {
      return new QNm((QName) o);
    }
  },

  /** NOTATION Type. */
  NOT("NOTATION", null, XSURI, false, false, false, false, false),

  /** Empty sequence type. */
  EMP("empty-sequence", null, EMPTY, false, false, false, false, false),

  /** Sequence type. */
  SEQ("sequence", null, EMPTY, false, false, false, false, false),

  /** Java type. */
  JAVA("java", null, EMPTY, true, true, true, false, false) {
    @Override
    public Item cast(final Item it, final QueryContext ctx,
        final InputInfo ii) {
      return new Jav(it);
    }
    @Override
    public Item cast(final Object o, final InputInfo ii) {
      return new Jav(o);
    }
  };

  /** Language pattern. */
  static final Pattern LANGPATTERN =
      Pattern.compile("[A-Za-z]{1,8}(-[A-Za-z0-9]{1,8})*");

  /** Parent type. */
  public final Type par;

  /** Number flag. */
  private final boolean num;
  /** Untyped flag. */
  private final boolean unt;
  /** String flag. */
  private final boolean str;
  /** Duration flag. */
  private final boolean dur;
  /** Date flag. */
  private final boolean dat;

  /** String representation. */
  private final byte[] string;
  /** URI representation. */
  private final byte[] uri;
  /** Sequence type. */
  private SeqType seq;

  /**
   * Constructor.
   * @param nm string representation
   * @param pr parent type
   * @param ur uri
   * @param n number flag
   * @param u untyped flag
   * @param s string flag
   * @param d duration flag
   * @param t date flag
   */
  AtomType(final String nm, final Type pr, final byte[] ur, final boolean n,
      final boolean u, final boolean s, final boolean d, final boolean t) {
    string = token(nm);
    par = pr;
    uri = ur;
    num = n;
    unt = u;
    str = s;
    dur = d;
    dat = t;
  }

  @Override
  public boolean isNumber() {
    return num;
  }

  @Override
  public boolean isString() {
    return str;
  }

  @Override
  public boolean isUntyped() {
    return unt;
  }

  @Override
  public boolean isDuration() {
    return dur;
  }

  @Override
  public boolean isDate() {
    return dat;
  }

  @Override
  public boolean isFunction() {
    return false;
  }

  @Override
  public boolean isMap() {
    return false;
  }

  @Override
  public byte[] string() {
    return string;
  }

  @Override
  public Item cast(final Item it, final QueryContext ctx, final InputInfo ii)
      throws QueryException {
    return it.type != this ? error(it, ii) : it;
  }

  @Override
  public Item cast(final Object o, final InputInfo ii) throws QueryException {
    Util.notexpected(o);
    return null;
  }

  @Override
  public SeqType seqType() {
    // cannot be statically instantiated due to circular dependencies
    if(seq == null) seq = new SeqType(this);
    return seq;
  }

  /**
   * Throws an exception if the specified item can't be converted to a number.
   * @param it item
   * @param ii input info
   * @return item argument
   * @throws QueryException query exception
   */
  Item checkNum(final Item it, final InputInfo ii)
      throws QueryException {
    final Type ip = it.type;
    return ip == URI || !ip.isString() && !ip.isNumber() &&
        !ip.isUntyped() && ip != BLN ? error(it, ii) : it;
  }

  /**
   * Checks the validity of the specified object and returns its long value.
   * @param o value to be checked
   * @param min minimum value
   * @param max maximum value
   * @param ii input info
   * @return integer value
   * @throws QueryException query exception
   */
  long checkLong(final Object o, final long min,
      final long max, final InputInfo ii) throws QueryException {

    final Item it = o instanceof Item ? (Item) o : Str.get(o.toString());
    checkNum(it, ii);

    final Type ip = it.type;
    if(ip == DBL || ip == FLT) {
      final double d = it.dbl(ii);
      if(Double.isNaN(d) || d == 1 / 0d || d == -1 / 0d) value(ii, this, it);
      if(d < Long.MIN_VALUE || d > Long.MAX_VALUE) INTRANGE.thrw(ii, d);
      if(min != max && (d < min || d > max)) FUNCAST.thrw(ii, this, it);
      return (long) d;
    }
    final long l = it.itr(ii);
    if(min == max) {
      final double d = it.dbl(ii);
      if(d < Long.MIN_VALUE || d > Long.MAX_VALUE)
        FUNCAST.thrw(ii, this, it);
    }
    if(min != max && (l < min || l > max)) FUNCAST.thrw(ii, this, it);
    return l;
  }

  /**
   * Checks if the specified item is a string.
   * @param it item
   * @return item argument
   */
  static boolean str(final Item it) {
    final Type ip = it.type;
    return (ip.isString() || ip.isUntyped()) && ip != URI;
  }

  /**
   * Checks the validity of the specified name.
   * @param it value to be checked
   * @param ii input info
   * @throws QueryException query exception
   * @return name
   */
  byte[] checkName(final Item it, final InputInfo ii)
      throws QueryException {
    final byte[] v = norm(it.string(ii));
    if(!XMLToken.isNCName(v)) error(it, ii);
    return v;
  }

  /**
   * Throws a casting exception.
   * @param it item to be included in the error message
   * @param ii input info
   * @return dummy item
   * @throws QueryException query exception
   */
  Item error(final Item it, final InputInfo ii)
      throws QueryException {
    Err.cast(ii, this, it);
    return null;
  }

  // PUBLIC AND STATIC METHODS ================================================

  @Override
  public final boolean instanceOf(final Type t) {
    return this == t || par != null && par.instanceOf(t);
  }

  @Override
  public final boolean isNode() {
    return false;
  }

  /**
   * Finds and returns the specified data type.
   * @param type type as string
   * @param atom atomic type
   * @return type or {@code null}
   */
  public static AtomType find(final QNm type, final boolean atom) {
    // type must be atomic, or must not have a namespace
    if(atom ^ type.uri().length == 0) {
      final byte[] ln = type.local();
      final byte[] uri = type.uri();
      for(final AtomType t : values()) {
        // skip non-standard types
        if(t == SEQ || t == JAVA) continue;
        if(eq(ln, t.string) && eq(uri, t.uri)) return t;
      }
    }
    return null;
  }

  @Override
  public int id() {
    return ordinal() + 32;
  }

  @Override
  public String toString() {
    final TokenBuilder tb = new TokenBuilder();
    if(uri == XSURI) tb.add(XS).add(':');
    tb.add(string);
    if(uri != XSURI) tb.add("()");
    return tb.toString();
  }
}
