// Returns true if the node matches the pattern func (m *CompiledMatch) EvalMatch(node xml.Node, mode string, context *ExecutionContext) bool { cur := node //false if wrong mode // #all is an XSLT 2.0 feature if m.Template != nil && mode != m.Template.Mode && m.Template.Mode != "#all" { return false } for i, step := range m.Steps { switch step.Op { case OP_END: return true case OP_ROOT: if cur.NodeType() != xml.XML_DOCUMENT_NODE { return false } case OP_ELEM: if cur.NodeType() != xml.XML_ELEMENT_NODE { return false } if step.Value != cur.Name() && step.Value != "*" { return false } case OP_NS: uri := "" // m.Template.Node if m.Template != nil { uri = context.LookupNamespace(step.Value, m.Template.Node) } else { uri = context.LookupNamespace(step.Value, nil) } if uri != cur.Namespace() { return false } case OP_ATTR: if cur.NodeType() != xml.XML_ATTRIBUTE_NODE { return false } if step.Value != cur.Name() && step.Value != "*" { return false } case OP_TEXT: if cur.NodeType() != xml.XML_TEXT_NODE && cur.NodeType() != xml.XML_CDATA_SECTION_NODE { return false } case OP_COMMENT: if cur.NodeType() != xml.XML_COMMENT_NODE { return false } case OP_ALL: if cur.NodeType() != xml.XML_ELEMENT_NODE { return false } case OP_PI: if cur.NodeType() != xml.XML_PI_NODE { return false } case OP_NODE: switch cur.NodeType() { case xml.XML_ELEMENT_NODE, xml.XML_CDATA_SECTION_NODE, xml.XML_TEXT_NODE, xml.XML_COMMENT_NODE, xml.XML_PI_NODE: // matches any of these node types default: return false } case OP_PARENT: cur = cur.Parent() if cur == nil { return false } case OP_ANCESTOR: next := m.Steps[i+1] if next.Op != OP_ELEM { return false } for { cur = cur.Parent() if cur == nil { return false } if next.Value == cur.Name() { break } } case OP_PREDICATE: // see test REC/5.2-16 // see test REC/5.2-22 evalFull := true if context != nil { prev := m.Steps[i-1] if prev.Op == OP_PREDICATE { prev = m.Steps[i-2] } if prev.Op == OP_ELEM || prev.Op == OP_ALL { parent := cur.Parent() sibs := context.ChildrenOf(parent) var clen, pos int for _, n := range sibs { if n.NodePtr() == cur.NodePtr() { pos = clen + 1 clen = clen + 1 } else { if n.NodeType() == xml.XML_ELEMENT_NODE { if n.Name() == cur.Name() || prev.Op == OP_ALL { clen = clen + 1 } } } } if step.Value == "last()" { if pos != clen { return false } } //eval predicate should do special number handling postest, err := strconv.Atoi(step.Value) if err == nil { if pos != postest { return false } } opos, olen := context.XPathContext.GetContextPosition() context.XPathContext.SetContextPosition(pos, clen) result := cur.EvalXPathAsBoolean(step.Value, context) context.XPathContext.SetContextPosition(opos, olen) if result == false { return false } evalFull = false } } if evalFull { //if we made it this far, fall back to the more expensive option of evaluating // the entire pattern globally //TODO: cache results on first run for given document xp := m.pattern if m.pattern[0] != '/' { xp = "//" + m.pattern } e := xpath.Compile(xp) o, err := node.Search(e) if err != nil { //fmt.Println("ERROR",err) } for _, n := range o { if cur.NodePtr() == n.NodePtr() { return true } } return false } case OP_ID: //TODO: fix lexer to only put literal inside step value val := strings.Trim(step.Value, "()\"'") id := cur.MyDocument().NodeById(val) if id == nil || node.NodePtr() != id.NodePtr() { return false } case OP_KEY: // TODO: make this robust if context != nil { val := strings.Trim(step.Value, "()") v := strings.Split(val, ",") keyname := strings.Trim(v[0], "\"'") keyval := strings.Trim(v[1], "\"'") key, _ := context.Style.Keys[keyname] if key != nil { o, _ := key.nodes[keyval] for _, n := range o { if cur.NodePtr() == n.NodePtr() { return true } } } } return false default: return false } } //in theory, OP_END means we never reach here // in practice, we can generate match patterns // that are missing OP_END due to how we handle OP_OR return true }