Package com.clarkparsia.pellet.datatypes.types.datetime

Source Code of com.clarkparsia.pellet.datatypes.types.datetime.RestrictedTimelineDatatype

package com.clarkparsia.pellet.datatypes.types.datetime;

import static java.lang.String.format;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeConstants;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;

import org.mindswap.pellet.exceptions.InternalReasonerException;

import aterm.ATermAppl;

import com.clarkparsia.pellet.datatypes.Datatype;
import com.clarkparsia.pellet.datatypes.EmptyRestrictedDatatype;
import com.clarkparsia.pellet.datatypes.Facet;
import com.clarkparsia.pellet.datatypes.OWLRealUtils;
import com.clarkparsia.pellet.datatypes.RestrictedDatatype;
import com.clarkparsia.pellet.datatypes.Facet.XSD;
import com.clarkparsia.pellet.datatypes.exceptions.InvalidConstrainingFacetException;
import com.clarkparsia.pellet.datatypes.types.real.ContinuousRealInterval;

/**
* <p>
* Title: Restricted time line datatype
* </p>
* <p>
* Description: A base implementation for datatypes based on the XSD 7 property
* date time model. Calendar objects are converted to real numbers based on the
* XML 1.1 datatype spec. This implementation uses two real number interval
* collections (one with time zone present, one with time zone absent).
* </p>
* <p>
* Copyright: Copyright (c) 2009
* </p>
* <p>
* Company: Clark & Parsia, LLC. <http://www.clarkparsia.com>
* </p>
*
* @author Mike Smith
*/
public class RestrictedTimelineDatatype implements RestrictedDatatype<XMLGregorianCalendar> {

  /*
   * TODO: Evaluate storing intervals in a tree to improve the efficiency of
   * #contains calls
   */

  private static final DatatypeFactory  dtFactory;
  private static final Logger        log;
  private static final BigInteger      SEC_PER_DAY;
  private static final BigInteger      SEC_PER_YEAR;
  private static final int        TZ_SHIFT;

  static {
    log = Logger.getLogger( RestrictedTimelineDatatype.class.getCanonicalName() );

    SEC_PER_DAY = BigInteger.valueOf( 86400 );
    SEC_PER_YEAR = BigInteger.valueOf( 31536000 );

    TZ_SHIFT = 14 * 60 * 60;

    try {
      dtFactory = DatatypeFactory.newInstance();
    } catch( DatatypeConfigurationException e ) {
      final String msg = "Failure initializing restricted timeline datatype support.";
      log.severe( msg );
      throw new InternalReasonerException( msg, e );
    }
  }

  private static Number calendarToReal(XMLGregorianCalendar c) {

    BigInteger yrPlusOne = c.getEonAndYear();
    final BigInteger y = (yrPlusOne == null)
      ? BigInteger.valueOf( 1971 )
      : yrPlusOne.subtract( BigInteger.ONE );
    if( yrPlusOne == null )
      yrPlusOne = BigInteger.valueOf( 1972 );

    final int month = (c.getMonth() == DatatypeConstants.FIELD_UNDEFINED)
      ? 12
      : c.getMonth();

    final int day = (c.getDay() == DatatypeConstants.FIELD_UNDEFINED)
      ? daysInMonth( yrPlusOne, month ) - 1
      : c.getDay() - 1;

    final int hour = (c.getHour() == DatatypeConstants.FIELD_UNDEFINED)
      ? 0
      : c.getHour();

    int minute = (c.getMinute() == DatatypeConstants.FIELD_UNDEFINED)
      ? 0
      : c.getMinute();

    final int second = (c.getSecond() == DatatypeConstants.FIELD_UNDEFINED)
      ? 0
      : c.getSecond();

    final BigDecimal fractionalSecond = c.getFractionalSecond();

    final int tz = c.getTimezone();
    if( tz != DatatypeConstants.FIELD_UNDEFINED )
      minute = minute - tz;

    BigInteger toTi;

    /*
     * Seconds in complete years
     */
    toTi = SEC_PER_YEAR.multiply( y );

    /*
     * Plus leap days
     */
    toTi = toTi
        .add( SEC_PER_DAY.multiply( y.divide( BigInteger.valueOf( 400 ) ).subtract(
            y.divide( BigInteger.valueOf( 100 ) ) ).add(
            y.divide( BigInteger.valueOf( 4 ) ) ) ) );

    int daySum = day;
    for( int m = 1; m < month; m++ )
      daySum += daysInMonth( yrPlusOne, m );

    /*
     * Seconds in complete days of current year
     */
    toTi = toTi.add( BigInteger.valueOf( 86400L * daySum ) );

    /*
     * Seconds in incomplete day
     */
    toTi = toTi.add( BigInteger.valueOf( 3600L * hour + 60L * minute + second ) );

    if( fractionalSecond == null || BigDecimal.ZERO.equals( fractionalSecond ) )
      return OWLRealUtils.getCanonicalObject( toTi );
    else
      return new BigDecimal( toTi ).add( fractionalSecond );
  }

  private static int daysInMonth(BigInteger year, int month) {
    if( month == 2 ) {
      if( year.remainder( BigInteger.valueOf( 4 ) ).equals( BigInteger.ZERO ) ) {
        if( year.remainder( BigInteger.valueOf( 100 ) ).equals( BigInteger.ZERO ) ) {
          if( year.remainder( BigInteger.valueOf( 400 ) ).equals( BigInteger.ZERO ) )
            return 29;
          else
            return 28;
        }
        else
          return 29;
      }
      else
        return 28;
    }
    else {
      switch ( month ) {
      case 4:
      case 6:
      case 9:
      case 11:
        return 30;
      case 1:
      case 3:
      case 5:
      case 7:
      case 8:
      case 10:
      case 12:
        return 31;
      default:
        throw new IllegalArgumentException();
      }
    }
  }

  public static DatatypeFactory getDatatypeFactory() {
    return dtFactory;
  }

  private static ContinuousRealInterval zoneShrink(ContinuousRealInterval i) {
    final Number lower = i.boundLower()
      ? OWLRealUtils.sum( i.getLower(), TZ_SHIFT )
      : null;
    final Number upper = i.boundUpper()
      ? OWLRealUtils.sum( i.getUpper(), -TZ_SHIFT )
      : null;
    if( lower != null && upper != null ) {
      final int cmp = OWLRealUtils.compare( lower, upper );
      if( cmp > 0 )
        return null;
      else if( cmp == 0 ) {
        if( !i.inclusiveLower() && !i.inclusiveUpper() )
          return null;
        else
          return new ContinuousRealInterval( lower );
      }
    }
    return new ContinuousRealInterval( lower, upper, i.inclusiveLower(), i.inclusiveUpper() );
  }

  protected final Datatype<? extends XMLGregorianCalendar>  datatype;

  protected final RestrictedDatatype<XMLGregorianCalendar>  empty;
  protected final boolean                    enumerable;
  protected final boolean                    finite;
  protected final List<ContinuousRealInterval>        nzIntervals;
  protected final QName                    schemaType;
  protected final List<ContinuousRealInterval>        wzIntervals;

  public RestrictedTimelineDatatype(Datatype<? extends XMLGregorianCalendar> datatype,
      QName schemaType, boolean requireTz) {
    this.datatype = datatype;
    this.schemaType = schemaType;
    this.empty = new EmptyRestrictedDatatype<XMLGregorianCalendar>( datatype );
    this.wzIntervals = Collections.singletonList( ContinuousRealInterval.allReals() );
    this.nzIntervals = requireTz
      ? Collections.<ContinuousRealInterval> emptyList()
      : Collections.singletonList( ContinuousRealInterval.allReals() );
    this.finite = false;
    this.enumerable = false;
  }

  private RestrictedTimelineDatatype(RestrictedTimelineDatatype other,
      List<ContinuousRealInterval> wzIntervals, List<ContinuousRealInterval> nzIntervals) {
    this.datatype = other.datatype;
    this.empty = other.empty;
    this.schemaType = other.schemaType;
    this.wzIntervals = Collections.unmodifiableList( wzIntervals );
    this.nzIntervals = Collections.unmodifiableList( nzIntervals );
    if( other.finite ) {
      this.finite = true;
    }
    else {
      boolean allFinite = true;
      for( ContinuousRealInterval i : wzIntervals ) {
        if( !i.isPoint() ) {
          allFinite = false;
          break;
        }
      }
      if( allFinite ) {
        for( ContinuousRealInterval i : wzIntervals ) {
          if( !i.isPoint() ) {
            allFinite = false;
            break;
          }
        }
      }
      this.finite = allFinite;
    }
    if( other.enumerable ) {
      this.enumerable = true;
    }
    else {
      boolean allEnumerable = nzIntervals.isEmpty();
      if( allEnumerable ) {
        for( ContinuousRealInterval i : wzIntervals ) {
          if( !i.isPoint() ) {
            allEnumerable = false;
            break;
          }
        }
      }
      this.enumerable = allEnumerable;
    }
  }

  public RestrictedDatatype<XMLGregorianCalendar> applyConstrainingFacet(ATermAppl facet,
      Object value) throws InvalidConstrainingFacetException {

    /*
     * Check the facet
     */
    Facet f = Facet.Registry.get( facet );
    if( f == null ) {
      final String msg = format(
          "Attempt to constrain datatype (%s) with unsupported constraining facet ('%s' , '%s')",
          getDatatype(), facet, value );
      log.severe( msg );
      throw new InvalidConstrainingFacetException( msg, facet, value );
    }

    /*
     * Check the value
     */
    XMLGregorianCalendar c = null;
    if( value instanceof XMLGregorianCalendar ) {
      c = (XMLGregorianCalendar) value;
    }
    if( c == null || !isValidValue( c ) ) {
      final String msg = format(
          "Attempt to constrain datatype (%s) using constraining facet ('%s') with an unsupported value ('%s')",
          getDatatype(), f, value );
      log.severe( msg );
      throw new InvalidConstrainingFacetException( msg, facet, value );
    }

    Number lower, upper;
    boolean inclusiveLower, inclusiveUpper;
    if( XSD.MAX_EXCLUSIVE.equals( f ) ) {
      lower = null;
      inclusiveLower = false;
      upper = calendarToReal( c );
      inclusiveUpper = false;
    }
    else if( XSD.MAX_INCLUSIVE.equals( f ) ) {
      lower = null;
      inclusiveLower = false;
      upper = calendarToReal( c );
      inclusiveUpper = true;
    }
    else if( XSD.MIN_EXCLUSIVE.equals( f ) ) {
      lower = calendarToReal( c );
      inclusiveLower = false;
      upper = null;
      inclusiveUpper = false;
    }
    else if( XSD.MIN_INCLUSIVE.equals( f ) ) {
      lower = calendarToReal( c );
      inclusiveLower = true;
      upper = null;
      inclusiveUpper = false;
    }
    else
      throw new IllegalStateException();

    ContinuousRealInterval wzRestriction, nzRestriction;
    {
      final ContinuousRealInterval restriction = new ContinuousRealInterval( lower, upper,
          inclusiveLower, inclusiveUpper );
      if( c.getTimezone() == DatatypeConstants.FIELD_UNDEFINED ) {
        nzRestriction = restriction;
        wzRestriction = zoneShrink( nzRestriction );
      }
      else {
        wzRestriction = restriction;
        nzRestriction = zoneShrink( wzRestriction );
      }
    }

    boolean changes = false;

    final List<ContinuousRealInterval> revisedWz = new ArrayList<ContinuousRealInterval>();
    if( wzRestriction == null ) {
      changes = wzIntervals.isEmpty();
    }
    else {
      for( ContinuousRealInterval i : wzIntervals ) {
        final ContinuousRealInterval j = i.intersection( wzRestriction );
        if( j != null ) {
          revisedWz.add( j );
          if( i != j )
            changes = true;
        }
        else
          changes = true;
      }
    }

    final List<ContinuousRealInterval> revisedNz = new ArrayList<ContinuousRealInterval>();
    if( nzRestriction == null ) {
      changes |= nzIntervals.isEmpty();
    }
    else {
      for( ContinuousRealInterval i : nzIntervals ) {
        final ContinuousRealInterval j = i.intersection( nzRestriction );
        if( j != null ) {
          revisedNz.add( j );
          if( i != j )
            changes = true;
        }
        else
          changes = true;
      }
    }

    if( changes ) {
      if( revisedWz.isEmpty() && revisedNz.isEmpty() )
        return empty;
      else
        return create( this, revisedWz, revisedNz );
    }
    else
      return this;
  }

  public boolean contains(Object value) {
    if( value instanceof XMLGregorianCalendar ) {
      final XMLGregorianCalendar c = (XMLGregorianCalendar) value;
      if( isValidValue( c ) ) {
        final Number n = calendarToReal( c );
        /*
         * TODO: This could be made more efficient by looking at how
         * each contained check fails (e.g., if intervals is sorted by
         * boundaries and n is not contained, but less than upper, there
         * is no need to look further).
         */
        for( ContinuousRealInterval i : c.getTimezone() == DatatypeConstants.FIELD_UNDEFINED
          ? nzIntervals
          : wzIntervals ) {
          if( i.contains( n ) )
            return true;
        }
        return false;
      }
      else
        return false;
    }
    else
      return false;
  }

  public boolean containsAtLeast(int n) {
    if( !finite || n <= 0 )
      return true;

    Number sum = 0;
    /*
     * TODO: This ignores excluded values, so may miscount
     */
    for( int i = 0; i < wzIntervals.size(); i++ ) {
      sum = OWLRealUtils.integerSum( sum, 28 * 60 + 1 );
      if( OWLRealUtils.compare( n, sum ) <= 0 )
        return true;
    }

    return false;
  }

  protected RestrictedTimelineDatatype create(RestrictedTimelineDatatype other,
      List<ContinuousRealInterval> wzIntervals, List<ContinuousRealInterval> nzIntervals) {
    return new RestrictedTimelineDatatype( other, wzIntervals, nzIntervals );
  }

  public RestrictedDatatype<XMLGregorianCalendar> exclude(Collection<?> values) {
    boolean changes = false;
    List<ContinuousRealInterval> revisedNz = new ArrayList<ContinuousRealInterval>( nzIntervals );

    for( Object o : values ) {
      if( o instanceof XMLGregorianCalendar ) {
        final XMLGregorianCalendar c = (XMLGregorianCalendar) o;
        if( c.getTimezone() == DatatypeConstants.FIELD_UNDEFINED ) {
          final Number n = calendarToReal( c );
          for( Iterator<ContinuousRealInterval> it = revisedNz.iterator(); it.hasNext(); ) {
            final ContinuousRealInterval i = it.next();
            if( i.contains( n ) ) {

              changes = true;
              it.remove();

              final ContinuousRealInterval less = i.less( n );
              if( less != null )
                revisedNz.add( less );

              final ContinuousRealInterval greater = i.greater( n );
              if( greater != null )
                revisedNz.add( greater );

              break;
            }
          }
        }
        else {
          /*
           * TODO: Exclusion of tz'd individuals requires storing the
           * individuals since the tz is used for identity
           */
          log.warning( "Exclusion of time zoned constants is not supported" );
        }
      }
    }

    if( changes ) {
      if( revisedNz.isEmpty() && wzIntervals.isEmpty() )
        return empty;
      else
        return create( this, wzIntervals, revisedNz );
    }
    else
      return this;
  }

  public Datatype<? extends XMLGregorianCalendar> getDatatype() {
    return datatype;
  }

  public XMLGregorianCalendar getValue(int i) {
    throw new UnsupportedOperationException();
  }

  public RestrictedDatatype<XMLGregorianCalendar> intersect(RestrictedDatatype<?> other,
      boolean negated) {

    if( other instanceof RestrictedTimelineDatatype ) {
      RestrictedTimelineDatatype otherRRD = (RestrictedTimelineDatatype) other;

      /*
       * Intersect zoned intervals
       */
      List<ContinuousRealInterval> revisedWz = new ArrayList<ContinuousRealInterval>();

      List<ContinuousRealInterval> intersectWithWz;
      if( negated ) {
        intersectWithWz = new ArrayList<ContinuousRealInterval>( Arrays
            .asList( ContinuousRealInterval.allReals() ) );
        for( ContinuousRealInterval i : otherRRD.wzIntervals ) {
          List<ContinuousRealInterval> tmp = new ArrayList<ContinuousRealInterval>();
          for( ContinuousRealInterval j : intersectWithWz ) {
            tmp.addAll( j.remove( i ) );
          }
          intersectWithWz = tmp;
        }
      }
      else
        intersectWithWz = otherRRD.wzIntervals;

      for( ContinuousRealInterval i : this.wzIntervals ) {
        for( ContinuousRealInterval j : intersectWithWz ) {
          ContinuousRealInterval k = i.intersection( j );
          if( k != null )
            revisedWz.add( k );
        }
      }

      /*
       * Intersect unzoned intervals
       */
      List<ContinuousRealInterval> revisedNz = new ArrayList<ContinuousRealInterval>();

      List<ContinuousRealInterval> intersectWithNz;
      if( negated ) {
        intersectWithNz = new ArrayList<ContinuousRealInterval>( Arrays
            .asList( ContinuousRealInterval.allReals() ) );
        for( ContinuousRealInterval i : otherRRD.nzIntervals ) {
          List<ContinuousRealInterval> tmp = new ArrayList<ContinuousRealInterval>();
          for( ContinuousRealInterval j : intersectWithNz ) {
            tmp.addAll( j.remove( i ) );
          }
          intersectWithNz = tmp;
        }
      }
      else
        intersectWithNz = otherRRD.nzIntervals;

      for( ContinuousRealInterval i : this.nzIntervals ) {
        for( ContinuousRealInterval j : intersectWithNz ) {
          ContinuousRealInterval k = i.intersection( j );
          if( k != null )
            revisedNz.add( k );
        }
      }

      /*
       * Return a data range based on the intersections
       */

      if( revisedWz.equals( this.wzIntervals ) && revisedNz.equals( this.nzIntervals ) )
        return this;
      else {
        if( revisedWz.isEmpty() && revisedNz.isEmpty() )
          return empty;
        else
          return create( this, revisedWz, revisedNz );
      }

    }
    else
      throw new IllegalArgumentException();
  }

  public boolean isEmpty() {
    return false;
  }

  public boolean isEnumerable() {
    return enumerable;
  }

  public boolean isFinite() {
    return finite;
  }

  protected boolean isValidValue(XMLGregorianCalendar c) {
    return schemaType.equals( c.getXMLSchemaType() );
  }

  public int size() {
    if( !finite )
      throw new IllegalStateException();

    Number sum = 0;
    for( int i = 0; i < wzIntervals.size(); i++ ) {
      sum = OWLRealUtils.integerSum( sum, 28 * 60 + 1 );
      if( OWLRealUtils.compare( Integer.MAX_VALUE, sum ) <= 0 )
        return Integer.MAX_VALUE;
    }
    return sum.intValue();
  }

  @Override
  public String toString() {
    /*
     * TODO: Reverse the date time to real mapping for debug printing
     */
    return format( "{%s,%s,%s}", datatype, wzIntervals, nzIntervals );
  }

  public RestrictedDatatype<XMLGregorianCalendar> union(RestrictedDatatype<?> other) {
    if( other instanceof RestrictedTimelineDatatype ) {
      RestrictedTimelineDatatype otherRRD = (RestrictedTimelineDatatype) other;

      /*
       * Union zoned intervals
       */

      List<ContinuousRealInterval> revisedWz = new ArrayList<ContinuousRealInterval>(
          this.wzIntervals );
      for( ContinuousRealInterval i : otherRRD.wzIntervals ) {
        List<ContinuousRealInterval> unionWith = new ArrayList<ContinuousRealInterval>();
        for( Iterator<ContinuousRealInterval> jt = revisedWz.iterator(); jt.hasNext(); ) {
          ContinuousRealInterval j = jt.next();
          if( i.canUnionWith( j ) ) {
            jt.remove();
            unionWith.add( j );
          }
        }
        if( unionWith.isEmpty() ) {
          revisedWz.add( i );
        }
        else {
          Set<ContinuousRealInterval> tmp = new HashSet<ContinuousRealInterval>();
          for( ContinuousRealInterval j : unionWith )
            tmp.addAll( i.union( j ) );
          revisedWz.addAll( tmp );
        }
      }

      /*
       * Union free intervals
       */

      List<ContinuousRealInterval> revisedNz = new ArrayList<ContinuousRealInterval>(
          this.nzIntervals );
      for( ContinuousRealInterval i : otherRRD.nzIntervals ) {
        List<ContinuousRealInterval> unionWith = new ArrayList<ContinuousRealInterval>();
        for( Iterator<ContinuousRealInterval> jt = revisedNz.iterator(); jt.hasNext(); ) {
          ContinuousRealInterval j = jt.next();
          if( i.canUnionWith( j ) ) {
            jt.remove();
            unionWith.add( j );
          }
        }
        if( unionWith.isEmpty() ) {
          revisedNz.add( i );
        }
        else {
          Set<ContinuousRealInterval> tmp = new HashSet<ContinuousRealInterval>();
          for( ContinuousRealInterval j : unionWith )
            tmp.addAll( i.union( j ) );
          revisedNz.addAll( tmp );
        }
      }

      return create( this, revisedWz, revisedNz );
    }
    else
      throw new IllegalArgumentException();
  }

  public Iterator<XMLGregorianCalendar> valueIterator() {
    /*
     * TODO: For point intervals of zone date times, this can be done
     */
    throw new UnsupportedOperationException();
  }

}
TOP

Related Classes of com.clarkparsia.pellet.datatypes.types.datetime.RestrictedTimelineDatatype

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.