#include "../../config/pathan_config.h" /** Copyright (c) 2001, DecisionSoft Limited All rights reserved. * Please see LICENSE.TXT for more information. */ #include "ATTimeOrDerivedImpl.hpp" #include #include #include #include #include #include #include #include "../../exceptions/XPath2TypeCastException.hpp" #include #include #include #include #include #include // for INT_MIN and INT_MAX #include // for atoi #include #include "../../utils/DateUtils.hpp" #include "../../utils/Date.hpp" ATTimeOrDerivedImpl:: ATTimeOrDerivedImpl(const XMLCh* typeURI, const XMLCh* typeName, const XMLCh* value, XPath2MemoryManager* memMgr, const DynamicContext* context): ATTimeOrDerived(memMgr), _typeName(typeName), _typeURI(typeURI) { setTime(value, context); } // private constructor for internal use ATTimeOrDerivedImpl::ATTimeOrDerivedImpl(const XMLCh* typeURI, const XMLCh* typeName, const ATDecimalOrDerived* hh, const ATDecimalOrDerived* mm, const ATDecimalOrDerived* ss, const Timezone* timezone, bool hasTimezone, XPath2MemoryManager* memMgr) : ATTimeOrDerived(memMgr), _hh(hh), _mm(mm), _ss(ss), _timezone(timezone), _hasTimezone(hasTimezone), _typeName(typeName), _typeURI(typeURI) { } /* Get the name of the primitive type (basic type) of this type * (ie "decimal" for xs:decimal) */ const XMLCh* ATTimeOrDerivedImpl::getPrimitiveTypeName() const { return this->getPrimitiveName(); } const XMLCh* ATTimeOrDerivedImpl::getPrimitiveName() { return XERCES_CPP_NAMESPACE_QUALIFIER SchemaSymbols::fgDT_TIME; } /* Get the name of this type (ie "integer" for xs:integer) */ const XMLCh* ATTimeOrDerivedImpl::getTypeName() const { return _typeName; } /* Get the namespace URI for this type */ const XMLCh* ATTimeOrDerivedImpl::getTypeURI() const { return _typeURI; } AnyAtomicType::AtomicObjectType ATTimeOrDerivedImpl::getTypeIndex() { return AnyAtomicType::TIME; } /* If possible, cast this type to the target type */ const AnyAtomicType* ATTimeOrDerivedImpl::castAsInternal(const XMLCh* targetURI, const XMLCh* targetType, const DynamicContext* context) const { XERCES_CPP_NAMESPACE_QUALIFIER XMLBuffer buf(1023, context->getMemoryManager()); const DatatypeFactory* target = context->getDatatypeFactory(targetURI, targetType); AnyAtomicType::AtomicObjectType targetIndex = target->getPrimitiveTypeIndex(); switch (targetIndex) { case DATE_TIME: { const ATDateOrDerived* currentDate = DateUtils::getCurrentDate(context); if(currentDate->getYears()->greaterThan(DatatypeFactory::POD2AT::createInteger(context->getMemoryManager(), 9999, context), context)) { buf.set(currentDate->getYears()->asString(context)); } else { buf.set(currentDate->getYears()->asString(4, context)); //pad to 4 digits } buf.append(XERCES_CPP_NAMESPACE_QUALIFIER chDash); buf.append(currentDate->getMonths()->asString(2, context)); buf.append(XERCES_CPP_NAMESPACE_QUALIFIER chDash); buf.append(currentDate->getDays()->asString(2, context)); buf.append(XERCES_CPP_NAMESPACE_QUALIFIER chLatin_T); buf.append(this->asString(context)); return DatatypeFactory::STR2AT::createDateTimeOrDerived(context->getMemoryManager(), targetURI, targetType, buf.getRawBuffer(), context); } case ANY_SIMPLE_TYPE: case UNTYPED_ATOMIC: //anySimpleType and untypedAtomic follow the same casting rules as string. case STRING: { return DatatypeFactory::STR2AT::createDerivedFromAtomicType(context->getMemoryManager(), targetURI, targetType, this->asLexicalString(context), context); } default: return AnyAtomicType::castAsInternal(targetURI, targetType, context); } } /* returns the XMLCh* (canonical) representation of this type */ const XMLCh* ATTimeOrDerivedImpl::asString(const DynamicContext* context) const { // since we actually store the time in its lexical representation all we have to // do is ask a normalized copy of this for it's lexical string and that will be our canonical const ATTimeOrDerivedImpl* canonicalTime = this; if (_hasTimezone) { canonicalTime = (const ATTimeOrDerivedImpl*) this->normalize(context); } return canonicalTime->asLexicalString(context); } /* returns the XMLCh* (lexical := prefix:localname) representation of this type */ const XMLCh* ATTimeOrDerivedImpl::asLexicalString(const DynamicContext* context) const { XERCES_CPP_NAMESPACE_QUALIFIER XMLBuffer buffer(1023, context->getMemoryManager()); buffer.append(this->_hh->asString(2, context)); buffer.append(XERCES_CPP_NAMESPACE_QUALIFIER chColon); buffer.append(this->_mm->asString(2, context)); buffer.append(XERCES_CPP_NAMESPACE_QUALIFIER chColon); if(this->_ss->lessThan(DatatypeFactory::POD2AT::createInteger(context->getMemoryManager(), 10, context), context)) { // TODO: deal with precision in a better way! buffer.append(XERCES_CPP_NAMESPACE_QUALIFIER chDigit_0); } if (this->_ss->equals(this->_ss->floor(context), context)) { const ATDecimalOrDerived* int_ss = (const ATDecimalOrDerived*) this->_ss->castAs(XERCES_CPP_NAMESPACE_QUALIFIER SchemaSymbols::fgURI_SCHEMAFORSCHEMA, XERCES_CPP_NAMESPACE_QUALIFIER SchemaSymbols::fgDT_INTEGER, context); buffer.append(int_ss->asString(context)); } else { buffer.append(this->_ss->asString(context)); } // Add timezone if exists if (this->_hasTimezone) { buffer.append(this->_timezone->asString(context)); } return context->getMemoryManager()->getPooledString(buffer.getRawBuffer()); } /* returns true if the two objects represent the same time, * false otherwise */ bool ATTimeOrDerivedImpl::equals(const AnyAtomicType* target, const DynamicContext* context) const { if(this->getPrimitiveTypeIndex() != target->getPrimitiveTypeIndex()) { DSLthrow(IllegalArgumentException,X("ATTimeOrDerivedImpl::equals"), X("Equality operator for given types not supported")); } const ATTimeOrDerivedImpl* myTimeCopy = (const ATTimeOrDerivedImpl*)this->normalize(context); const ATTimeOrDerivedImpl* otherTimeCopy = (const ATTimeOrDerivedImpl*)((const ATTimeOrDerived*)target)->normalize(context); return ( myTimeCopy->_hh->equals(otherTimeCopy->_hh, context) && myTimeCopy->_mm->equals(otherTimeCopy->_mm, context) && myTimeCopy->_ss->equals(otherTimeCopy->_ss, context) ); } /** Releases the memory used by this Item */ void ATTimeOrDerivedImpl::release() const { //TODO needs to check for ownership before releasing! getMemoryManager()->deallocate((void*)this); } /** * Returns true i and only if this time is greater than the given time. */ bool ATTimeOrDerivedImpl::greaterThan(const ATTimeOrDerived* other, const DynamicContext* context) const { ATTimeOrDerivedImpl* myTimeCopy = (ATTimeOrDerivedImpl*)this->normalize(context); ATTimeOrDerivedImpl* otherTimeCopy = (ATTimeOrDerivedImpl*)other->normalize(context); if ((myTimeCopy->_hh->greaterThan(otherTimeCopy->_hh, context)) || (myTimeCopy->_hh->equals(otherTimeCopy->_hh, context) && myTimeCopy->_mm->greaterThan(otherTimeCopy->_mm, context)) || (myTimeCopy->_hh->equals(otherTimeCopy->_hh, context) && myTimeCopy->_mm->equals(otherTimeCopy->_mm, context) && myTimeCopy->_ss->greaterThan(otherTimeCopy->_ss, context)) ) { return true; } else { return false; } } /** * Returns true if and only if this time is less than the given time. */ bool ATTimeOrDerivedImpl::lessThan(const ATTimeOrDerived* other, const DynamicContext* context) const { ATTimeOrDerivedImpl* myTimeCopy = (ATTimeOrDerivedImpl*)this->normalize(context); ATTimeOrDerivedImpl* otherTimeCopy = (ATTimeOrDerivedImpl*)other->normalize(context); if ((myTimeCopy->_hh->lessThan(otherTimeCopy->_hh, context)) || (myTimeCopy->_hh->equals(otherTimeCopy->_hh, context) && myTimeCopy->_mm->lessThan(otherTimeCopy->_mm, context)) || (myTimeCopy->_hh->equals(otherTimeCopy->_hh, context) && myTimeCopy->_mm->equals(otherTimeCopy->_mm, context) && myTimeCopy->_ss->lessThan(otherTimeCopy->_ss, context)) ) { return true; } else { return false; } } /** * Returns an integer representing the hour component of this object */ const ATDecimalOrDerived* ATTimeOrDerivedImpl::getHours() const { return _hh; } /** * Returns an integer representing the minute component of this object */ const ATDecimalOrDerived* ATTimeOrDerivedImpl::getMinutes() const { return _mm; } /** * Returns an decimal representing the second component of this object */ const ATDecimalOrDerived* ATTimeOrDerivedImpl::getSeconds() const { return _ss; } /** * Returns true if the timezone is defined for this object, false otherwise. */ bool ATTimeOrDerivedImpl::hasTimezone() const { return _hasTimezone; } /** * Returns the timezone associated with this object, or * null, if the timezone is not set */ const Timezone* ATTimeOrDerivedImpl::getTimezone() const { return _timezone; } /** * Setter for timezone. Overrides the current timezone. (Not to be * confused with addTimezone(). If passed NULL, timezone is removed (unset) */ const ATTimeOrDerived* ATTimeOrDerivedImpl::setTimezone(const Timezone* timezone, const DynamicContext* context) const { bool hasTimezone = timezone == 0 ? false : true; return new (context->getMemoryManager()) ATTimeOrDerivedImpl(this->getTypeURI(), this->getTypeName(), this->_hh, this->_mm, this->_ss, timezone, hasTimezone, context->getMemoryManager()); } /** * Returns an ATTimeOrDerived with a timezone added to it */ const ATTimeOrDerived* ATTimeOrDerivedImpl::addTimezone(const ATDurationOrDerived* timezone, const DynamicContext* context) const { const Timezone* tz = new (context->getMemoryManager()) Timezone(timezone, context); // If this time does not have a timezone, add the given timezone if (!_hasTimezone) { ATTimeOrDerived* timeCopy = (ATTimeOrDerived*)this->setTimezone(tz, context); return timeCopy; } else { //else convert the time into an equivalent one with given timezone // Minutes MAPM offset = tz->getMinutes()-_timezone->getMinutes(); MAPM temp = this->_mm->asMAPM() + offset; const ATDecimalOrDerived* mm = DatatypeFactory::POD2AT::createNonNegativeInteger(context->getMemoryManager(), DateUtils::modulo(temp, DateUtils::g_minutesPerHour), context); MAPM carry = (temp / DateUtils::g_minutesPerHour).floor(); // Hours offset = tz->getHours()-_timezone->getHours(); temp = this->_hh->asMAPM() + offset + carry; const ATDecimalOrDerived* hh = DatatypeFactory::POD2AT::createNonNegativeInteger(context->getMemoryManager(), DateUtils::modulo(temp, DateUtils::g_hoursPerDay), context); return new (context->getMemoryManager()) ATTimeOrDerivedImpl(this->getTypeURI(), this->getTypeName(), hh, mm, this->_ss, tz, true, context->getMemoryManager()); } } /** * Returns a time with the given dayTimeDuration added to it */ const ATTimeOrDerived* ATTimeOrDerivedImpl::addDayTimeDuration(const ATDurationOrDerived* dayTime, const DynamicContext* context) const { if(dayTime->isNegative()) { return subtractDayTimeDuration(dayTime->getHours()->asMAPM(), dayTime->getMinutes()->asMAPM(), dayTime->getSeconds()->asMAPM(), context); } else { return addDayTimeDuration(dayTime->getHours()->asMAPM(), dayTime->getMinutes()->asMAPM(), dayTime->getSeconds()->asMAPM(), context); } } const ATTimeOrDerived* ATTimeOrDerivedImpl::normalize(const DynamicContext* context) const { const Timezone* timezone; if (!_hasTimezone) { timezone = new (context->getMemoryManager()) Timezone(context->getImplicitTimezone(), context); } else { timezone = this->_timezone; } // Minutes MAPM tzMinutes = timezone->getMinutes(); MAPM temp = this->_mm->asMAPM() - tzMinutes; const ATDecimalOrDerived* mm = DatatypeFactory::POD2AT::createNonNegativeInteger(context->getMemoryManager(), DateUtils::modulo(temp, DateUtils::g_minutesPerHour), context); MAPM carry = (temp / DateUtils::g_minutesPerHour).floor(); // Hours MAPM tzHours = timezone->getHours(); temp = this->_hh->asMAPM() - tzHours + carry; const ATDecimalOrDerived* hh = DatatypeFactory::POD2AT::createNonNegativeInteger(context->getMemoryManager(), DateUtils::modulo(temp, DateUtils::g_hoursPerDay), context); return new (context->getMemoryManager()) ATTimeOrDerivedImpl(this->getTypeURI(), this->getTypeName(), hh, mm, this->_ss, new (context->getMemoryManager()) Timezone(0, 0), true, // timezone set to UTC context->getMemoryManager()); } /** * Returns a time with the given dayTimeDuration subtracted from it */ const ATTimeOrDerived* ATTimeOrDerivedImpl::subtractDayTimeDuration(const ATDurationOrDerived* dayTime, const DynamicContext* context) const { if(dayTime->isNegative()) { return addDayTimeDuration(dayTime->getHours()->asMAPM(), dayTime->getMinutes()->asMAPM(), dayTime->getSeconds()->asMAPM(), context); } else { return subtractDayTimeDuration(dayTime->getHours()->asMAPM(), dayTime->getMinutes()->asMAPM(), dayTime->getSeconds()->asMAPM(), context); } } const ATTimeOrDerived* ATTimeOrDerivedImpl::addDayTimeDuration(MAPM hours, MAPM minutes, MAPM seconds, const DynamicContext* context) const { const ATTimeOrDerivedImpl* time = this; if (_hasTimezone) { time = (const ATTimeOrDerivedImpl*)this->normalize(context); } // Seconds MAPM temp = time->_ss->asMAPM() + seconds; MAPM ss = DateUtils::modulo(temp, DateUtils::g_secondsPerMinute); MAPM carry = (temp/DateUtils::g_secondsPerMinute).floor(); // Minutes temp = time->_mm->asMAPM() + minutes + carry; MAPM mm = DateUtils::modulo(temp, DateUtils::g_minutesPerHour); carry = (temp/DateUtils::g_minutesPerHour).floor(); // Hours temp = time->_hh->asMAPM() + hours + carry; MAPM hh = DateUtils::modulo(temp, DateUtils::g_hoursPerDay); return new (context->getMemoryManager()) ATTimeOrDerivedImpl(this->_typeURI, this->_typeName, DatatypeFactory::POD2AT::createNonNegativeInteger(context->getMemoryManager(), hh, context), DatatypeFactory::POD2AT::createNonNegativeInteger(context->getMemoryManager(), mm, context), DatatypeFactory::POD2AT::createDecimal(context->getMemoryManager(), ss, context), time->getTimezone(), time->hasTimezone(), context->getMemoryManager()); } const ATTimeOrDerived* ATTimeOrDerivedImpl::subtractDayTimeDuration(MAPM hours, MAPM minutes, MAPM seconds, const DynamicContext* context) const { return this->addDayTimeDuration(hours.neg(), minutes.neg(), seconds.neg(), context); } /** * Returns a dayTimeDuration corresponding to the difference between this * and the given ATTimeOrDerived* */ const ATDurationOrDerived* ATTimeOrDerivedImpl::subtractTime(const ATTimeOrDerived* time, const DynamicContext* context) const { // normalize both times first const ATTimeOrDerived* thisTime = this->normalize(context); const ATTimeOrDerived* otherTime = time->normalize(context); // calculate the differences in seconds MAPM thisSeconds = thisTime->getHours()->asMAPM() * DateUtils::g_secondsPerHour + thisTime->getMinutes()->asMAPM() * DateUtils::g_secondsPerMinute + thisTime->getSeconds()->asMAPM(); MAPM otherSeconds = otherTime->getHours()->asMAPM() * DateUtils::g_secondsPerHour + otherTime->getMinutes()->asMAPM() * DateUtils::g_secondsPerMinute + otherTime->getSeconds()->asMAPM(); MAPM secDiff = thisSeconds - otherSeconds; bool isNegative = (secDiff < MM_Zero); MAPM endDiff = secDiff.abs(); // getDays const ATDecimalOrDerived* DD = DatatypeFactory::POD2AT::createInteger(context->getMemoryManager(), (endDiff / DateUtils::g_secondsPerDay).floor(), context); MAPM carry = DateUtils::modulo(endDiff,DateUtils::g_secondsPerDay); // get hour const ATDecimalOrDerived* hh = DatatypeFactory::POD2AT::createInteger(context->getMemoryManager(), (carry / DateUtils::g_secondsPerHour).floor(), context); // get minute const ATDecimalOrDerived* mm = DatatypeFactory::POD2AT::createInteger(context->getMemoryManager(), (DateUtils::modulo(carry, DateUtils::g_secondsPerHour) / DateUtils::g_secondsPerMinute).floor(), context); // get seconds const ATDecimalOrDerived* ss = DatatypeFactory::POD2AT::createDecimal(context->getMemoryManager(), DateUtils::modulo( DateUtils::modulo(carry, DateUtils::g_secondsPerHour) , DateUtils::g_secondsPerMinute), context); XERCES_CPP_NAMESPACE_QUALIFIER XMLBuffer buffer(1023, context->getMemoryManager()); if(isNegative) { buffer.append(XERCES_CPP_NAMESPACE_QUALIFIER chDash); } buffer.append(XERCES_CPP_NAMESPACE_QUALIFIER chLatin_P); buffer.append(DD->asString(context)); buffer.append(XERCES_CPP_NAMESPACE_QUALIFIER chLatin_D); buffer.append(XERCES_CPP_NAMESPACE_QUALIFIER chLatin_T); buffer.append(hh->asString(context)); buffer.append(XERCES_CPP_NAMESPACE_QUALIFIER chLatin_H); buffer.append(mm->asString(context)); buffer.append(XERCES_CPP_NAMESPACE_QUALIFIER chLatin_M); buffer.append(ss->asString(context)); buffer.append(XERCES_CPP_NAMESPACE_QUALIFIER chLatin_S); return DatatypeFactory::STR2AT::createDayTimeDuration(context->getMemoryManager(), buffer.getRawBuffer(), context); } AnyAtomicType::AtomicObjectType ATTimeOrDerivedImpl::getPrimitiveTypeIndex() const { return this->getTypeIndex(); } void ATTimeOrDerivedImpl::setTime(const XMLCh* const time, const DynamicContext* context) { unsigned int length = XERCES_CPP_NAMESPACE_QUALIFIER XMLString::stringLen(time); if(time == NULL) { DSLthrow(XPath2TypeCastException,X("XSTimeImpl::setTime"), X("Invalid representation of time")); } // State variables etc. bool gotDot = false; bool gotDigit = false; unsigned int pos = 0; long int tmpnum = 0; double decplace = 1; double tmpdec = 0; unsigned int numDigit = 0; // defaulting values MAPM hh = 0; MAPM mm = 0; MAPM ss = 0; _hasTimezone = false; bool zonepos = false; int zonehh = 0; int zonemm = 0; int state = 3 ; // 3 = hour // 4 = minutes / 5 = sec / 6 = timezonehour / 7 = timezonemin XMLCh tmpChar; bool wrongformat = false; pos = 0; while ( ! wrongformat && pos < length) { tmpChar = time[pos]; pos++; switch(tmpChar) { case L'.': { if (! gotDot && gotDigit && state == 5 && numDigit == 2) { gotDot = true; ss = tmpnum; gotDigit = false; tmpnum = 0; break; } wrongformat = true; break; } case 0x0030: case 0x0031: case 0x0032: case 0x0033: case 0x0034: case 0x0035: case 0x0036: case 0x0037: case 0x0038: case 0x0039: { if ( gotDot && state == 5) { decplace *= 10; } else { numDigit ++; } tmpnum *= 10; tmpnum += static_cast(tmpChar - 0x0030); gotDigit = true; break; } case L'-' : { if ( gotDigit && state == 5 && numDigit == 2) { tmpdec = tmpnum / decplace; ss += tmpdec; gotDigit = false; _hasTimezone = true; zonepos = false; _hasTimezone = true; tmpnum = 0; numDigit = 0; }else { wrongformat = true; } state ++; break; } case L'+' : { if ( gotDigit && state == 5 && numDigit == 2) { tmpdec = tmpnum / decplace; ss += tmpdec; state = 6; gotDigit = false; _hasTimezone = true; zonepos = true; tmpnum = 0; numDigit = 0; } else { wrongformat = true; } break; } case L':' : { if (gotDigit ) { if (state == 3 && numDigit == 2) { hh = tmpnum; tmpnum = 0; numDigit = 0; gotDigit = false; } else if ( state == 4 && numDigit == 2) { mm = tmpnum; tmpnum = 0; numDigit = 0; gotDigit = false; } else if ( state == 6 && numDigit == 2) { zonehh = tmpnum; _hasTimezone = true; tmpnum = 0; numDigit = 0; gotDigit = false; } else { wrongformat = true; } state ++; }else { wrongformat = true; } break; } case L'Z' : { if (gotDigit && state == 5 && numDigit == 2) { tmpdec = tmpnum / decplace; ss += tmpdec; state = 8; // final state _hasTimezone = true; gotDigit = false; tmpnum = 0; numDigit = 0; } else { wrongformat = true; } break; } default: wrongformat = true; } } if (gotDigit) { if ( gotDigit && state == 7 && numDigit == 2) { zonemm = tmpnum; _hasTimezone = true; }else if ( gotDigit && state == 5 && numDigit == 2) { tmpdec = tmpnum / decplace; ss += tmpdec; }else { wrongformat = true; } } if ( hh > 24 || mm > 60 || ss >= 61 || zonehh > 24 || zonemm > 60 ) { wrongformat = true; } if ( wrongformat) { DSLthrow(XPath2TypeCastException,X("XSTimeImpl::setTime"), X("Invalid representation of time")); } // Create Timezone object, clean this up in future if (zonepos == false) { zonehh *= -1; zonemm *= -1; } _timezone = new (getMemoryManager()) Timezone(zonehh, zonemm); _hh = DatatypeFactory::POD2AT::createNonNegativeInteger(getMemoryManager(), hh, context); _mm = DatatypeFactory::POD2AT::createNonNegativeInteger(getMemoryManager(), mm, context); _ss = DatatypeFactory::POD2AT::createDecimal(getMemoryManager(), ss, context); }