/* * Copyright 1999-2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // Class header file. #include "XPathExpression.hpp" #include #include #if defined(XALAN_CLASSIC_IOSTREAMS) #include #else #include #endif #include #include #include "XObjectTypeCallback.hpp" XALAN_CPP_NAMESPACE_BEGIN #if defined(XALAN_INLINE_INITIALIZATION) && !defined(XALAN_INLINE_INITIALIZATION_IS_DEFINITION_BUG) const XPathExpression::TokenQueueSizeType XPathExpression::s_opCodeMapLengthIndex; #endif // This is an array which indicates which of the value defined by // XPathExpression::eOpCodes are node tests. static const bool theNodeTestOpCodesArray[] = { false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true, true, true, true, true, true, true, true, true, true, true, true, false, false, false, true, true, true }; static const unsigned int theNodeTestOpCodesArraySize = sizeof(theNodeTestOpCodesArray) / sizeof(theNodeTestOpCodesArray[0]); // This is an array for all of the opcode defined by XPathExpression::eOpCodes. // It is offset by 3, because there opcode values are -3, -2, and -1. So, // when getting the length of an opcode, we add three to get the correct index. static const int theOpCodeLengthArray[] = { 1, 1, 1, 0, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 3, XPathExpression::s_opCodeMapLengthIndex + 3, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, 1, 1, 1, 1, 1, 1, 1, XPathExpression::s_opCodeMapLengthIndex + 2, XPathExpression::s_opCodeMapLengthIndex + 2, XPathExpression::s_opCodeMapLengthIndex + 2, XPathExpression::s_opCodeMapLengthIndex + 2, XPathExpression::s_opCodeMapLengthIndex + 2, XPathExpression::s_opCodeMapLengthIndex + 2, XPathExpression::s_opCodeMapLengthIndex + 2, XPathExpression::s_opCodeMapLengthIndex + 2, XPathExpression::s_opCodeMapLengthIndex + 2, XPathExpression::s_opCodeMapLengthIndex + 2, XPathExpression::s_opCodeMapLengthIndex + 2, XPathExpression::s_opCodeMapLengthIndex + 2, XPathExpression::s_opCodeMapLengthIndex + 2, XPathExpression::s_opCodeMapLengthIndex + 2, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 2, XPathExpression::s_opCodeMapLengthIndex + 2, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1, XPathExpression::s_opCodeMapLengthIndex + 1 }; static const int theOpCodeLengthArraySize = sizeof(theOpCodeLengthArray) / sizeof(theOpCodeLengthArray[0]); inline int getOpCodeLength(int theOpCode) { assert(theOpCodeLengthArraySize == XPathExpression::eOpCodeNextAvailable + 3); // Add 3 to get the correct index into the array... theOpCode += 3; if (theOpCode < 0 || theOpCode >= theOpCodeLengthArraySize) { return 0; } else { return theOpCodeLengthArray[theOpCode]; } } XPathExpression::XPathExpressionException::XPathExpressionException(const XalanDOMString& theMessage, MemoryManagerType& theManager) : XalanXPathException(theMessage, theManager) { } XPathExpression::XPathExpressionException::~XPathExpressionException() { } XPathExpression::InvalidOpCodeException::InvalidOpCodeException(OpCodeMapValueType theOpCode, XalanDOMString& theResult) : XPathExpressionException(FormatErrorMessage(theOpCode, theResult), theResult.getMemoryManager()) { } XPathExpression::InvalidOpCodeException::~InvalidOpCodeException() { } XalanDOMString& XPathExpression::InvalidOpCodeException::FormatErrorMessage( OpCodeMapValueType theOpCode, XalanDOMString& theResult) { XalanDOMString theOpcode(theResult.getMemoryManager()); LongToDOMString(theOpCode, theOpcode); return XalanMessageLoader::getMessage( theResult, XalanMessages::InvalidOpcodeWasDetected_1Param, theOpcode); } XPathExpression::InvalidArgumentCountException::InvalidArgumentCountException( OpCodeMapValueType theOpCode, OpCodeMapValueType theExpectedCount, OpCodeMapValueType theSuppliedCount, XalanDOMString& theResult) : XPathExpressionException( FormatErrorMessage( theOpCode, theExpectedCount, theSuppliedCount, theResult), theResult.getMemoryManager()) { } XPathExpression::InvalidArgumentCountException::~InvalidArgumentCountException() { } XalanDOMString& XPathExpression::InvalidArgumentCountException::FormatErrorMessage( OpCodeMapValueType theOpCode, OpCodeMapValueType theExpectedCount, OpCodeMapValueType theSuppliedCount, XalanDOMString& theBuffer) { XalanDOMString theResult(theBuffer.getMemoryManager()); LongToDOMString(theOpCode, theResult); XalanDOMString theResult1(theBuffer.getMemoryManager()); LongToDOMString(theExpectedCount, theResult1); XalanDOMString theResult2(theBuffer.getMemoryManager()); LongToDOMString(theSuppliedCount, theResult2); return XalanMessageLoader::getMessage( theBuffer , XalanMessages::InvalidNumberOfArgsWasDetected_3Param, theResult1, theResult2); } XPathExpression::InvalidArgumentException::InvalidArgumentException( OpCodeMapValueType theOpCode, OpCodeMapValueType theValue, XalanDOMString& theResult) : XPathExpressionException( FormatErrorMessage( theOpCode, theValue, theResult), theResult.getMemoryManager()) { } XPathExpression::InvalidArgumentException::~InvalidArgumentException() { } XalanDOMString& XPathExpression::InvalidArgumentException::FormatErrorMessage( OpCodeMapValueType theOpCode, OpCodeMapValueType theValue, XalanDOMString& theBuffer) { XalanDOMString theResult(theBuffer.getMemoryManager()); LongToDOMString(theValue, theResult); XalanDOMString theResult1(theBuffer.getMemoryManager()); UnsignedLongToDOMString(theOpCode, theResult1); return XalanMessageLoader::getMessage( theBuffer, XalanMessages::InvalidNumberOfArgsWasSupplied_2Param, theResult1) ; } static const XalanDOMString s_emptyString(XalanMemMgrs::getDummyMemMgr()); XPathExpression::XPathExpression(MemoryManagerType& theManager) : m_opMap(theManager), m_lastOpCodeIndex(0), m_tokenQueue(theManager), m_currentPosition(0), m_currentPattern(&s_emptyString), m_numberLiteralValues(theManager) { m_opMap.reserve(eDefaultOpMapSize); m_tokenQueue.reserve(eDefaultTokenQueueSize); } XPathExpression::~XPathExpression() { reset(); } void XPathExpression::reset() { XALAN_USING_STD(fill); XALAN_USING_STD(for_each); m_opMap.clear(); m_tokenQueue.clear(); } void XPathExpression::shrink() { if (m_opMap.capacity() > m_opMap.size()) { OpCodeMapType tmp(m_opMap, m_opMap.getMemoryManager()); tmp.swap(m_opMap); } } #if defined(XALAN_XPATH_EXPRESSION_USE_ITERATORS) XPathExpression::OpCodeMapValueType XPathExpression::getOpCodeLengthFromOpMap( OpCodeMapPositionType opPos, MemoryManagerType& theManager) const { assert(opPos - getInitialOpCodePosition() >= 0 && opPos - getInitialOpCodePosition() < opCodeMapSize()); OpCodeMapValueType theResult = 0; // Is there a valid opcode? const int theOpCodeLength = getOpCodeLength(*opPos); if (theOpCodeLength == 0) { XalanDOMString theResult(theManager); throw InvalidOpCodeException(-1, theResult); } else { // Does the Op code have a length > 1? if (theOpCodeLength > 1) { // Yes, so get the length. theResult = *(opPos + s_opCodeMapLengthIndex); } } return theResult; } #endif XPathExpression::OpCodeMapValueType XPathExpression::getOpCodeLengthFromOpMap( #if defined(XALAN_XPATH_EXPRESSION_USE_ITERATORS) OpCodeMapSizeType theIndex, #else OpCodeMapPositionType theIndex, #endif MemoryManagerType& theManager) const { OpCodeMapValueType theResult = 0; if (theIndex >= opCodeMapSize()) { XalanDOMString theResult(theManager); throw InvalidOpCodeException(-1, theResult); } else { // Is there a valid opcode? const int theOpCodeLength = getOpCodeLength(m_opMap[theIndex]); if (theOpCodeLength == 0) { XalanDOMString theResult(theManager); throw InvalidOpCodeException(-1, theResult); } else { // Does the Op code have a length > 1? if (theOpCodeLength > 1) { // Yes, so get the length. theResult = m_opMap[theIndex + s_opCodeMapLengthIndex]; } } } return theResult; } void XPathExpression::setOpCodeArgs( eOpCodes theOpCode, OpCodeMapSizeType theIndex, const OpCodeMapValueVectorType& theArgs) { // There must be at least enough space to hold the OpCode // and the length indicator. assert(opCodeMapSize() > theIndex + 1); const int theOpCodeLength = getOpCodeLength(theOpCode); // Make sure it's a valid op code and that it // matches the op code at supplied index. if (theOpCodeLength == 0 || m_opMap[theIndex] != theOpCode) { XalanDOMString theResult( getMemoryManager() ); throw InvalidOpCodeException(theOpCode, theResult); } else { const OpCodeMapValueType theArgCount = theOpCodeLength - 1 - s_opCodeMapLengthIndex; if (OpCodeMapValueVectorType::size_type(theArgCount) != theArgs.size()) { XalanDOMString theResult( getMemoryManager() ); throw InvalidArgumentCountException(theOpCode, theOpCodeLength, theArgCount, theResult); } else { assert(opCodeMapSize() > theIndex + theArgCount); for (OpCodeMapValueType i = 0; i < theArgCount; ++i) { if (theArgs[i] < 0) { XalanDOMString theResult( getMemoryManager() ); throw InvalidArgumentException(theOpCode, theArgs[i], theResult); } else { m_opMap[i + theIndex + s_opCodeMapLengthIndex + 1] = theArgs[i]; } } } } } XPathExpression::OpCodeMapSizeType XPathExpression::appendOpCode(eOpCodes theOpCode) { const int theOpCodeLength = getOpCodeLength(theOpCode); if (theOpCodeLength == 0) { XalanDOMString theResult( getMemoryManager() ); throw InvalidOpCodeException(theOpCode, theResult); } else { // Set the current index before pushing, by // getting the length, which will be correct // after the push. m_lastOpCodeIndex = opCodeMapSize(); m_opMap.push_back(theOpCode); assert(s_opCodeMapLengthIndex == 1); if (theOpCodeLength > 1) { m_opMap.push_back(theOpCodeLength); for (int i = 2; i < theOpCodeLength; ++i) { m_opMap.push_back(eENDOP); } } if (m_lastOpCodeIndex != 0) { m_opMap[s_opCodeMapLengthIndex] += theOpCodeLength; } } assert(opCodeMapSize() == OpCodeMapSizeType(opCodeMapLength())); return m_lastOpCodeIndex; } void XPathExpression::replaceOpCode( OpCodeMapSizeType theIndex, eOpCodes theOldOpCode, eOpCodes theNewOpCode) { if (theIndex >= opCodeMapSize() || m_opMap[theIndex] != theOldOpCode || getOpCodeLength(theOldOpCode) != getOpCodeLength(theNewOpCode)) { XalanDOMString theResult( getMemoryManager() ); throw InvalidOpCodeException(theNewOpCode, theResult); } else { m_opMap[theIndex] = theNewOpCode; } } XPathExpression::OpCodeMapValueType XPathExpression::insertOpCode( eOpCodes theOpCode, OpCodeMapSizeType theIndex) { const int theOpCodeLength = getOpCodeLength(theOpCode); if (theOpCodeLength == 0) { XalanDOMString theResult( getMemoryManager() ); throw InvalidOpCodeException(theOpCode, theResult); } else { // Insert -1 for each element of the opcode. m_opMap.insert( m_opMap.begin() + theIndex, OpCodeMapSizeType(theOpCodeLength), -1); // Set the member that contains the last insert/appended // index. m_lastOpCodeIndex = theIndex; // Assign the opcode. m_opMap[theIndex] = theOpCode; // Update the entire expression length. m_opMap[s_opCodeMapLengthIndex] += theOpCodeLength; } return theOpCodeLength; } void XPathExpression::updateShiftedOpCodeLength( OpCodeMapValueType theOpCode, #if defined(NDEBUG) OpCodeMapSizeType /* theOriginalIndex */, #else OpCodeMapSizeType theOriginalIndex, #endif OpCodeMapSizeType theNewIndex) { // There must be some other expressions in // the buffer... assert(opCodeMapSize() > theNewIndex + 1); assert(theNewIndex > theOriginalIndex); const OpCodeMapValueType theOpCodeLength = getOpCodeLength(theOpCode); // Make sure it's a valid op code and that it // matches the op code at supplied index. if (theOpCodeLength == 0 || m_opMap[theNewIndex] != theOpCode) { XalanDOMString theResult( getMemoryManager() ); throw InvalidOpCodeException(theOpCode, theResult); } else { // Determine where the length position of the op code // is. const OpCodeMapSizeType theLengthIndex = theNewIndex + s_opCodeMapLengthIndex; // Too long, then throw an exception. if (theLengthIndex >= opCodeMapSize()) { XalanDOMString theResult( getMemoryManager() ); throw InvalidOpCodeException(theOpCode, theResult); } else { // Look ahead to where the new operand is. const OpCodeMapSizeType theNextOpCodePosition = getNextOpCodePosition(theNewIndex); // If it's there, then update our size by // adding that size in. if (theNextOpCodePosition < opCodeMapSize()) { m_opMap[theLengthIndex] += getOpCodeLengthFromOpMap(theNextOpCodePosition, getMemoryManager()); } } } } void XPathExpression::updateOpCodeLength( OpCodeMapValueType theOpCode, OpCodeMapValueType theIndex) { // There must be some other expressions in // the buffer... assert(opCodeMapSize() > theIndex + 1); const int theOpCodeLength = getOpCodeLength(theOpCode); // Make sure it's a valid op code and that it // matches the op code at supplied index. if (theOpCodeLength == 0 || m_opMap[theIndex] != theOpCode) { XalanDOMString theResult( getMemoryManager() ); throw InvalidOpCodeException(theOpCode, theResult); } else { // This presumes that the other opcodes // have been appended to the expression, // and that the specified op code's length // needs to be set. The size includes the // normal length of the opcode, plus the // length of its subexpressions. m_opMap[theIndex + s_opCodeMapLengthIndex] = opCodeMapLength() - theIndex; } } bool XPathExpression::isNodeTestOpCode(OpCodeMapValueType theOpCode) { if (theOpCode < theNodeTestOpCodesArraySize) { return theNodeTestOpCodesArray[theOpCode]; } else { return false; } } void XPathExpression::updateOpCodeLengthAfterNodeTest(OpCodeMapSizeType theIndex) { // There must be some other expressions in // the buffer... assert(opCodeMapSize() > theIndex + 1); const OpCodeMapValueType theOpCode = m_opMap[theIndex]; const int theOpCodeLength = getOpCodeLength(theOpCode); // Make sure it's a valid op code and that it // is a node test op code. if (theOpCodeLength == 0 || isNodeTestOpCode(theOpCode) == false) { XalanDOMString theResult( getMemoryManager() ); throw InvalidOpCodeException(theOpCode, theResult); } else { m_opMap[theIndex + s_opCodeMapLengthIndex + 1] = opCodeMapLength() - theIndex; } } void XPathExpression::dumpOpCodeMap( PrintWriter& thePrintWriter, OpCodeMapSizeType theStartPosition) const { for (OpCodeMapSizeType i = theStartPosition; i < opCodeMapSize(); i++) { thePrintWriter.print(" '"); thePrintWriter.print(m_opMap[i]); thePrintWriter.print("'"); } } void XPathExpression::dumpOpCodeMap( OstreamType& theStream, OpCodeMapSizeType theStartPosition) const { for (OpCodeMapSizeType i = theStartPosition; i < opCodeMapSize(); i++) { theStream << " '" << m_opMap[i] << "'"; } } void XPathExpression::dumpTokenQueue( OstreamType& theStream, TokenQueueSizeType theStartPosition) const { for (TokenQueueSizeType i = theStartPosition; i < tokenQueueSize(); i++) { assert(getToken(i) != 0); theStream << " '" << getToken(i)->str() << "'"; } } void XPathExpression::dumpTokenQueue( PrintWriter& thePrintWriter, TokenQueueSizeType theStartPosition) const { for (TokenQueueSizeType i = theStartPosition; i < tokenQueueSize(); i++) { assert(getToken(i) != 0); if (i > 0) { thePrintWriter.print(" '"); } else { thePrintWriter.print("'"); } thePrintWriter.print(getToken(i)->str()); thePrintWriter.print("'"); } } void XPathExpression::dumpRemainingTokenQueue(PrintWriter& thePrintWriter) const { XalanDOMString tmpString(thePrintWriter.getMemoryManager()); thePrintWriter.print( XalanMessageLoader::getMessage( tmpString, XalanMessages::RemainingTokens)); thePrintWriter.print("("); dumpTokenQueue( thePrintWriter, m_currentPosition); thePrintWriter.print(")"); } void XPathExpression::dumpRemainingTokenQueue( OstreamType& theStream, MemoryManager& theMemoryManager) const { XalanDOMString tmpString(theMemoryManager); theStream << XalanMessageLoader::getMessage(tmpString, XalanMessages::RemainingTokens) << "("; dumpTokenQueue(theStream, m_currentPosition); theStream << ")"; } void XPathExpression::pushArgumentOnOpCodeMap(const XToken& theXToken) { assert(m_currentPosition != 0); const int thePosition = m_currentPosition - 1; assert(thePosition < tokenQueueSize()); // Set the entry in the token queue to the XObject. m_tokenQueue[thePosition] = theXToken; // Push the index onto the op map. m_opMap.push_back(thePosition); // Update the op map length. ++m_opMap[s_opCodeMapLengthIndex]; } void XPathExpression::pushArgumentOnOpCodeMap(const XalanDOMString& theToken) { assert(m_currentPosition != 0); const TokenQueueSizeType thePosition = m_currentPosition - 1; assert(thePosition < tokenQueueSize()); // Set the entry in the token queue to the XObject. m_tokenQueue[thePosition].set(theToken, getMemoryManager()); // Push the index onto the op map. m_opMap.push_back(thePosition); // Update the op map length. ++m_opMap[s_opCodeMapLengthIndex]; } void XPathExpression::pushArgumentOnOpCodeMap( double theNumber, const XalanDOMString& theString) { assert(m_currentPosition != 0); const TokenQueueSizeType thePosition = m_currentPosition - 1; assert(thePosition < tokenQueueSize()); // Set the entry in the token queue to the XObject. m_tokenQueue[thePosition].set(theNumber, theString, getMemoryManager()); // Push the index onto the op map. m_opMap.push_back(thePosition); // Update the op map length. ++m_opMap[s_opCodeMapLengthIndex]; } void XPathExpression::pushNumberLiteralOnOpCodeMap(double theNumber) { // Get the new index for the literal... const OpCodeMapValueType theIndex = OpCodeMapValueType(m_numberLiteralValues.size()); assert(NumberLiteralValueVectorType::size_type(theIndex) == m_numberLiteralValues.size()); // Push the index onto the op map. m_opMap.push_back(theIndex); // Update the op map length. ++m_opMap[s_opCodeMapLengthIndex]; m_numberLiteralValues.push_back(theNumber); } void XPathExpression::pushCurrentTokenOnOpCodeMap() { assert(m_currentPosition != 0); const TokenQueueSizeType thePosition = m_currentPosition - 1; assert(thePosition < tokenQueueSize()); // Push the index onto the op map. m_opMap.push_back(thePosition); // Update the op map length. ++m_opMap[s_opCodeMapLengthIndex]; } XALAN_CPP_NAMESPACE_END