// ToUnsigned static bool ToUnsigned( const COpcString& cString, ULong& nValue, ULong nMax, ULong nMin ) { // extract base COpcText cText; COpcTextReader cReader(cString); UINT uBase = 10; cText.SetType(COpcText::Literal); cText.SetText(L"0x"); cText.SetIgnoreCase(); if (cReader.GetNext(cText)) { uBase = 16; } // read unsigned value. return ToUnsigned(cReader.GetBuf(), nValue, nMax, nMin, uBase); }
// CheckForDelim bool COpcTextReader::CheckForDelim(COpcText& cToken, UINT uPosition) { // check for new line delim. if (cToken.GetNewLineDelim()) { if (m_szBuf[uPosition] == L'\n' || m_szBuf[uPosition] == L'\r') { cToken.SetNewLine(); cToken.SetDelimChar(m_szBuf[uPosition]); return true; } } // check for one of the delim chars. LPCWSTR szDelims = cToken.GetDelims(); UINT uCount = (szDelims != NULL)?wcslen(szDelims):0; for (UINT ii = 0; ii < uCount; ii++) { if (IsEqual(cToken, m_szBuf[uPosition], szDelims[ii])) { cToken.SetDelimChar(szDelims[ii]); return true; } } return false; }
// FindDelimited bool COpcTextReader::FindDelimited(COpcText& cToken) { OPC_ASSERT(m_szBuf != NULL); OPC_ASSERT(m_uLength != 0); // skip leading whitespace UINT uPosition = SkipWhitespace(cToken); // check if there is still data left to read. if (uPosition >= m_uEndOfData) { return false; } // read until a delimiter. for (UINT ii = uPosition; ii < m_uEndOfData; ii++) { // check if search halted. if (CheckForHalt(cToken, ii)) { return false; } // check if delimiter found. if (CheckForDelim(cToken, ii)) { // copy token - empty tokens are valid. CopyData(cToken, uPosition, ii); return true; } } // check for end of data - true if EOF is a delim. if (ii >= m_uEndOfData) { cToken.SetEof(); if (cToken.GetEofDelim()) { CopyData(cToken, uPosition, ii); return true; } } return false; }
// FindLiteral bool COpcTextReader::FindLiteral(COpcText& cToken) { OPC_ASSERT(m_szBuf != NULL); OPC_ASSERT(m_uLength != 0); LPCWSTR szText = cToken.GetText(); UINT uLength = (szText != NULL)?wcslen(szText):0; // check for trivial case if (uLength == 0) { return false; } UINT uPosition = SkipWhitespace(cToken); // check if there is enough data. if (uLength > (m_uEndOfData - uPosition)) { return false; } for (UINT ii = uPosition; ii < m_uEndOfData-uLength+1; ii++) { // check if search halted. if (CheckForHalt(cToken, ii)) { return false; } // compare text at current position. if (IsEqual(cToken, m_szBuf+ii, szText, uLength)) { CopyData(cToken, ii, ii+uLength); return true; } // stop search if leading unmatching characters are not ignored. if (!cToken.GetSkipLeading()) { break; } } return false; }
// ToSigned static bool ToSigned( const COpcString& cString, Long& nValue, Long nMax, Long nMin ) { nValue = 0; // extract plus sign bool bSign = true; COpcText cText; COpcTextReader cReader(cString); cText.SetType(COpcText::Literal); cText.SetText(L"+"); if (!cReader.GetNext(cText)) { // extract minus sign cText.SetType(COpcText::Literal); cText.SetText(L"-"); bSign = !cReader.GetNext(cText); } // read unsigned value. ULong uValue = 0; if (!ToUnsigned(cReader.GetBuf(), uValue, (bSign)?nMax:-nMin, 0, 10)) { return false; } nValue = (Long)uValue; // apply sign. if (!bSign) { nValue = -nValue; } return true; }
// Read template<> bool OpcXml::Read(const COpcString& cString, Decimal& cValue) { bool bResult = true; TRY { COpcText cText; COpcTextReader cReader(cString); // parse whole integer portion. cText.SetType(COpcText::Delimited); cText.SetDelims(L"."); cText.SetEofDelim(); if (!cReader.GetNext(cText)) { THROW_(bResult, false); } // convert to signed integer. Long nValue = 0; if (!ToSigned(cText, nValue, MAX_DECIMAL, MIN_DECIMAL)) { THROW_(bResult, false); } cValue.int64 = nValue*10000; if (cText.GetDelimChar() == L'.') { // parse decimal portion. cText.SetType(COpcText::Delimited); cText.SetEofDelim(); if (!cReader.GetNext(cText)) { THROW_(bResult, false); } // convert to unsigned integer. ULong uValue = 0; if (!ToUnsigned(cText, uValue, MAX_DEC_FRACTION, 0, 10)) { THROW_(bResult, false); } cValue.int64 += (Long)uValue; } } CATCH { cValue.int64 = 0; } return bResult; }
// ToReal static bool ToReal( const COpcString& cString, Double& nValue, Double nMax, Double nMin ) { bool bResult = true; TRY { // extract non-whitespace token. COpcText cText; COpcTextReader cReader(cString); cText.SetType(COpcText::NonWhitespace); cText.SetSkipLeading(); if (!cReader.GetNext(cText)) { THROW_(bResult, false); } // parse double with C runtime function. WCHAR* pzEnd = NULL; nValue = (Double)wcstod((LPCWSTR)(COpcString&)cText, &pzEnd); // check for error - all text must have been parsed. if (pzEnd == NULL || wcslen(pzEnd) != 0) { THROW_(bResult, false); } // check limits. if (nValue > nMax && nValue < nMin) THROW_(bResult, false); } CATCH { nValue = 0; } return bResult; }
// FindWhitespace bool COpcTextReader::FindWhitespace(COpcText& cToken) { OPC_ASSERT(m_szBuf != NULL); OPC_ASSERT(m_uLength != 0); UINT uPosition = 0; // skip leading non-whitespace if (cToken.GetSkipLeading()) { for (UINT ii = 0; ii < m_uEndOfData; ii++) { if (CheckForHalt(cToken, ii)) { return false; } if (iswspace(m_szBuf[ii])) { uPosition = ii; break; } } } // check if there is still data left to read. if (uPosition >= m_uEndOfData) { return false; } // read until a non-whitespace. for (UINT ii = uPosition; ii < m_uEndOfData; ii++) { if (CheckForHalt(cToken, ii)) { break; } if (!iswspace(m_szBuf[ii])) { break; } } // check for empty token if (ii == uPosition) { return false; } // copy token. CopyData(cToken, uPosition, ii); return true; }
// IsEqual static bool IsEqual(COpcText& cToken, WCHAR zValue1, WCHAR zValue2) { if (cToken.GetIgnoreCase()) { WCHAR z1 = (iswlower(zValue1))?towupper(zValue1):zValue1; WCHAR z2 = (iswlower(zValue2))?towupper(zValue2):zValue2; return (z1 == z2); } return (zValue1 == zValue2); }
// IsEqual static bool IsEqual(COpcText& cToken, LPCWSTR szValue1, LPCWSTR szValue2, UINT uSize = -1) { if (szValue1 == NULL || szValue2 == NULL) { return (szValue1 == szValue2); } else if (uSize == -1 && cToken.GetIgnoreCase()) { return (wcsicmp(szValue1, szValue2) == 0); } else if (uSize == -1) { return (wcscmp(szValue1, szValue2) == 0); } else if (cToken.GetIgnoreCase()) { return (wcsnicmp(szValue1, szValue2, uSize) == 0); } return (wcsncmp(szValue1, szValue2, uSize) == 0); }
// FindToken bool COpcTextReader::FindToken(COpcText& cToken) { OPC_ASSERT(m_szBuf != NULL); OPC_ASSERT(m_uLength != 0); switch (cToken.GetType()) { case COpcText::Literal: return FindLiteral(cToken); case COpcText::NonWhitespace: return FindNonWhitespace(cToken); case COpcText::Whitespace: return FindWhitespace(cToken); case COpcText::Delimited: return FindDelimited(cToken); } return false; }
// ToDate static bool ToDate( const COpcString& cString, WORD& wYear, WORD& wMonth, WORD& wDay ) { bool bResult = true; TRY { COpcText cText; COpcTextReader cReader(cString); ULong uValue = 0; // parse year field. cText.SetType(COpcText::Delimited); cText.SetDelims(L"-"); if (!cReader.GetNext(cText)) THROW_(bResult, false); if (!ToUnsigned(cText, uValue, MAX_YEAR, MIN_YEAR, 10)) THROW_(bResult, false); wYear = (WORD)uValue; // parse month field. cText.SetType(COpcText::Delimited); cText.SetDelims(L"-"); if (!cReader.GetNext(cText)) THROW_(bResult, false); if (!ToUnsigned(cText, uValue, MAX_MONTH, MIN_MONTH, 10)) THROW_(bResult, false); wMonth = (WORD)uValue; // parse day field. cText.SetType(COpcText::Delimited); cText.SetEofDelim(); if (!cReader.GetNext(cText)) THROW_(bResult, false); if (!ToUnsigned(cText, uValue, MAX_DAY, MIN_DAY, 10)) THROW_(bResult, false); wDay = (WORD)uValue; } CATCH { wYear = 0; wMonth = 0; wDay = 0; } return bResult; }
// SkipWhitespace UINT COpcTextReader::SkipWhitespace(COpcText& cToken) { if (!cToken.GetSkipWhitespace()) { return 0; } for (UINT ii = 0; ii < m_uEndOfData; ii++) { if (CheckForHalt(cToken, ii) || !iswspace(m_szBuf[ii])) { return ii; } } return m_uEndOfData; }
// ToOffset static bool ToOffset( const COpcString& cString, bool bNegative, SHORT& sMinutes ) { bool bResult = true; TRY { COpcText cText; COpcTextReader cReader(cString); ULong uValue = 0; // parse hour field. cText.SetType(COpcText::Delimited); cText.SetDelims(L":"); if (!cReader.GetNext(cText)) THROW_(bResult, false); if (!ToUnsigned(cText, uValue, MAX_HOUR, MIN_HOUR, 10)) THROW_(bResult, false); sMinutes = (SHORT)uValue*60; // parse minute field. cText.SetType(COpcText::Delimited); cText.SetDelims(L"."); cText.SetEofDelim(); if (!cReader.GetNext(cText)) THROW_(bResult, false); if (!ToUnsigned(cText, uValue, MAX_MINUTE, MIN_MINUTE, 10)) THROW_(bResult, false); sMinutes += (SHORT)uValue; // add sign. if (bNegative) { sMinutes = -sMinutes; } } CATCH { sMinutes = 0; } return bResult; }
// CheckForHalt bool COpcTextReader::CheckForHalt(COpcText& cToken, UINT uPosition) { // check if max chars exceeded. if (cToken.GetMaxChars() > 0) { if (cToken.GetMaxChars() <= uPosition) { return false; } } // check for end of data - halts if EOF is not a delim. if (uPosition >= m_uEndOfData) { cToken.SetEof(); return !cToken.GetEofDelim(); } // check for one of halt characters. LPCWSTR szHaltChars = cToken.GetHaltChars(); if (szHaltChars == NULL) { return false; } UINT uCount = wcslen(szHaltChars); for (UINT ii = 0; ii < uCount; ii++) { if (IsEqual(cToken, m_szBuf[uPosition], szHaltChars[ii])) { cToken.SetHaltChar(szHaltChars[ii]); return true; } } return false; }
// ToTime static bool ToTime( const COpcString& cString, WORD& wHour, WORD& wMinute, WORD& wSeconds, WORD& wFraction ) { bool bResult = true; TRY { COpcText cText; COpcTextReader cReader(cString); ULong uValue = 0; // parse hour field. cText.SetType(COpcText::Delimited); cText.SetDelims(L":"); if (!cReader.GetNext(cText)) THROW_(bResult, false); if (!ToUnsigned(cText, uValue, MAX_HOUR, MIN_HOUR, 10)) THROW_(bResult, false); wHour = (WORD)uValue; // parse month field. cText.SetType(COpcText::Delimited); cText.SetDelims(L":"); if (!cReader.GetNext(cText)) THROW_(bResult, false); if (!ToUnsigned(cText, uValue, MAX_MINUTE, MIN_MINUTE, 10)) THROW_(bResult, false); wMinute = (WORD)uValue; // parse seconds field. cText.SetType(COpcText::Delimited); cText.SetDelims(L"."); cText.SetEofDelim(); if (!cReader.GetNext(cText)) THROW_(bResult, false); if (!ToUnsigned(cText, uValue, MAX_SECOND, MIN_SECOND, 10)) THROW_(bResult, false); wSeconds = (WORD)uValue; // parse seconds fraction field. wFraction = 0; if (cText.GetDelimChar() == L'.') { cText.SetType(COpcText::Delimited); cText.SetEofDelim(); if (!cReader.GetNext(cText)) THROW_(bResult, false); // preprocess text. COpcString cFraction = cText; // add trailing zeros. while (cFraction.GetLength() < 3) cFraction += _T("0"); // truncate extra digits. if (cFraction.GetLength() > 3) cFraction = cFraction.SubStr(0,3); if (!ToUnsigned(cFraction, uValue, MAX_ULONG, 0, 10)) { THROW_(bResult, false); } // result is in milliseconds. wFraction = (WORD)uValue; } } CATCH { wHour = 0; wMinute = 0; wSeconds = 0; wFraction = 0; } return bResult; }
// FindEnclosed bool COpcTextReader::FindEnclosed(COpcText& cToken) { OPC_ASSERT(m_szBuf != NULL); OPC_ASSERT(m_uLength != 0); WCHAR zStart = 0; WCHAR zEnd = 0; cToken.GetBounds(zStart, zEnd); // skip leading whitespace UINT uPosition = SkipWhitespace(cToken); // check if there is still data left to read. if (uPosition >= m_uEndOfData) { return false; } // read until finding the start delimiter, for (UINT ii = uPosition; ii < m_uEndOfData; ii++) { // check if search halted. if (CheckForHalt(cToken, ii)) { return false; } // check for start character. if (IsEqual(cToken, m_szBuf[ii], zStart)) { uPosition = ii; break; } } // check if there is still data left to read. if (ii >= m_uEndOfData) { return false; } // read until finding the end delimiter, for (ii = uPosition+1; ii < m_uEndOfData; ii++) { // check if search halted. if (CheckForHalt(cToken, ii)) { return false; } // check for end character. if (IsEqual(cToken, m_szBuf[ii], zStart)) { // ignore if character is escaped. if (cToken.GetAllowEscape() && (uPosition < ii-1)) { if (m_szBuf[ii-1] == L'\\') { continue; } } // copy token - empty tokens are valid. CopyData(cToken, uPosition+1, ii); return true; } } return false; }
// CopyData void COpcTextReader::CopyData(COpcText& cToken, UINT uStart, UINT uEnd) { cToken.CopyData(m_szBuf+uStart, uEnd-uStart); cToken.SetStart(uStart); cToken.SetEnd(uEnd-1); }
// ToUnsigned static bool ToUnsigned( const COpcString& cString, ULong& nValue, ULong nMax, ULong nMin, UINT uBase ) { bool bResult = true; TRY { COpcText cText; COpcTextReader cReader(cString); // extract non-whitespace. cText.SetType(COpcText::NonWhitespace); cText.SetEofDelim(); if (!cReader.GetNext(cText)) { THROW_(bResult, false); } COpcString cValue = cText; nValue = 0; for (UINT ii = 0; ii < cValue.GetLength(); ii++) { UINT uDigit = CharToValue(cValue[ii], uBase); // invalid digit found. if (uDigit == -1) { bResult = false; break; } // detect overflow if (nValue > nMax/uBase) THROW_(bResult, false); // shift result up by base. nValue *= uBase; // detect overflow if (nValue > nMax - uDigit) THROW_(bResult, false); // add digit. nValue += uDigit; } // detect underflow if (nMin > nValue) THROW_(bResult, false); } CATCH { nValue = 0; } return bResult; }
// Read template<> bool OpcXml::Read(const COpcString& cString, DateTime& cValue) { bool bResult = true; FILETIME cFileTime; // check for invalid date. if (cString.IsEmpty()) { cValue = OpcMinDate(); return true; } TRY { SYSTEMTIME cSystemTime; ZeroMemory(&cSystemTime, sizeof(cSystemTime)); COpcText cText; COpcTextReader cReader(cString); // parse date fields. cText.SetType(COpcText::Delimited); cText.SetDelims(L"T"); if (!cReader.GetNext(cText)) THROW_(bResult, false); if (!ToDate(cText, cSystemTime.wYear, cSystemTime.wMonth, cSystemTime.wDay)) { THROW_(bResult, false); } // parse time fields. cText.SetType(COpcText::Delimited); cText.SetDelims(L"Z-+"); cText.SetEofDelim(); if (!cReader.GetNext(cText)) THROW_(bResult, false); bResult = ToTime( cText, cSystemTime.wHour, cSystemTime.wMinute, cSystemTime.wSecond, cSystemTime.wMilliseconds); if (!bResult) { THROW_(bResult, false); } // convert to a UTC file time. if (!SystemTimeToFileTime(&cSystemTime, &cFileTime)) { THROW_(bResult, false); } if (cText.GetDelimChar() != _T('Z')) { // convert local system time to UTC file time. if (cText.GetEof()) { FILETIME ftUtcTime; if (!OpcLocalTimeToUtcTime(cFileTime, ftUtcTime)) { THROW_(bResult, false); } cFileTime = ftUtcTime; } // apply offset specified in the datetime string. else { bool bNegative = (cText.GetDelimChar() == _T('-')); // parse time fields. cText.SetType(COpcText::Delimited); cText.SetEofDelim(); if (!cReader.GetNext(cText)) THROW_(bResult, false); SHORT sMinutes = 0; bResult = ToOffset( cText, bNegative, sMinutes); if (!bResult) { THROW_(bResult, false); } // apply timezone offset. LONGLONG llTime = OpcToInt64(cFileTime); llTime -= ((LONGLONG)sMinutes)*60*10000000; cFileTime = OpcToFILETIME(llTime); } } cValue = cFileTime; } CATCH { ZeroMemory(&cFileTime, sizeof(cFileTime)); cValue = cFileTime; } return bResult; }
// GetNext bool COpcTextReader::GetNext(COpcText& cToken) { // no more data to get - give up if (m_uEndOfData == 0) { return false; } // find the token if (!FindToken(cToken)) { return false; } // all done if token is not being extracted. if (cToken.GetNoExtract()) { return true; } UINT uEndOfToken = cToken.GetEnd() + 1; UINT uDataLeft = m_uEndOfData - uEndOfToken; // extract the delimiter if extracting token. // new line delimiter found. if (cToken.GetNewLine()) { if (cToken.GetDelimChar() == _T('\r')) { uEndOfToken += 2; uDataLeft -= 2; } else { uEndOfToken += 1; uDataLeft -= 1; } } // specific delimiter found. else if (cToken.GetDelimChar() > 0 && !cToken.GetEof()) { uEndOfToken++; uDataLeft--; } // move leftover data to the start of the buffer for (UINT ii = 0; ii < uDataLeft; ii++) { m_szBuf[ii] = m_szBuf[uEndOfToken+ii]; } m_szBuf[ii] = L'\0'; m_uEndOfData = uDataLeft; // set EOF flag if no data left in buffer. if (m_uEndOfData == 0) { cToken.SetEof(); } return true; }