docs: Object getValue(
      MatchSpaceKey msg,
      Object contextValue)
      throws MatchingException, BadMessageFormatMatchingException
    {
      if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled())
        tc.entry(
          cclass,
          "getValue",
          "msg: " + msg + "contextValue: " + contextValue);
      
      // The value which we'll return    
      SetValEvaluationContext xpEval = null;
    
      // May need to call MFP multiple times, if our context has multiple nodes
      if(contextValue != null)
      {
        // Currently this must be a list of nodes
        if (contextValue instanceof SetValEvaluationContext)
        {
          SetValEvaluationContext evalContext = (SetValEvaluationContext)contextValue;
  
          // Get the node list
          ArrayList wrappedParentList = evalContext.getWrappedNodeList();
          
          // If the list is empty, then we have yet to get the document root
          if(wrappedParentList.isEmpty())
          {
      	    //Set up a root (document) context for evaluation
          	Object docRoot = Matching.getEvaluator().getDocumentRoot(msg);
              
            // Create an object to hold the wrapped node
            WrappedNodeResults wrapper = new WrappedNodeResults(docRoot);
            // Set the root into the evaluation context
            evalContext.addNode(wrapper);
          }
          
          // Iterate over the nodes        
          Iterator iter = wrappedParentList.iterator();
  
          while(iter.hasNext())
          {
            WrappedNodeResults nextWrappedParentNode = (WrappedNodeResults)iter.next();       
            Object nextParentNode = nextWrappedParentNode.getNode();
            
            String debugString = "";
            if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled())
              tc.debug(this,cclass, "getValue", "Evaluation context node: " + nextWrappedParentNode); 
            
            ArrayList wrappedChildList = nextWrappedParentNode.getEvalListResult(id.getName());
              
            // If not cached we'll need to go to MFP
            if(wrappedChildList == null)
            {
              // Call MFP to get the results for this node
              ArrayList nodeList = 
                (ArrayList) msg.getIdentifierValue(id,
                                                   false,
                                                   nextParentNode, 
                                                   true); // true means return a nodelist
              wrappedChildList = new ArrayList();
              
              if(nodeList != null)
              {
                Iterator iterNode = nodeList.iterator();
                
                while(iterNode.hasNext())
                {
                  Object theNode = (Object)iterNode.next();
  
                  // Create an object to hold the wrapped node
                  WrappedNodeResults wrapper = new WrappedNodeResults(theNode);
                  wrappedChildList.add(wrapper); 
                }              
              }
              // Add result to cache for next time
              nextWrappedParentNode.addEvalListResult(id.getName(), wrappedChildList);
              
              if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled())
                debugString = "Result from MFP ";
            }
            else
            {
              if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled())
                debugString = "Result from Cache ";
            }
  
            // Useful debug
            if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled())
            {
              Iterator iterDebug = wrappedChildList.iterator();
              String asString = ", type: List - ";
              while(iterDebug.hasNext())
              {
                WrappedNodeResults wrapper = (WrappedNodeResults)iterDebug.next();
                asString = asString + "<" + wrapper + ">"; 
              }
              tc.debug(this,cclass, "getValue", debugString + "for identifier " + id.getName() + asString);
            }          
            
            if(!wrappedChildList.isEmpty())
            {
              // Build the return value as an XPathEvaluationContext
              if(xpEval == null)
                xpEval = new SetValEvaluationContext();
    
              // Add the wrapped node to the result
              xpEval.addWrappedNodeList(wrappedChildList);                      
            } 
          } // eof while
        } // eof instanceof XPathEvaluationContext
      }
      
      if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled())
        tc.exit(this,cclass, "getValue", xpEval);
      
      return xpEval;
    }