/**
* Copyright 2016 StreamSets Inc.
*
* Licensed under the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package com.streamsets.pipeline.lib.xml.xpath;
import com.google.common.base.Strings;
import com.streamsets.pipeline.api.impl.XMLChar;
import com.streamsets.pipeline.lib.xml.Constants;
import javax.xml.stream.events.XMLEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class XPathMatchingEventTracker {
private final Map<String, String> namespaces = new HashMap<>();
private final ArrayList<ElementMatcher> matchersByDepth = new ArrayList<>();
private int depth = 0;
private int matchesThroughDepth = 0;
public XPathMatchingEventTracker(String xPath, Map<String, String> namespaces) {
if (namespaces != null) {
this.namespaces.putAll(namespaces);
}
if (xPath != null) {
boolean ignoreNamespaces;
if (xPath != null && XMLChar.isValidName(xPath)) {
//this is simply a field name with no "/" separator; adjust it to support previous syntax
xPath = Constants.ROOT_ELEMENT_PATH + Constants.PATH_SEPARATOR + xPath;
ignoreNamespaces = true;
} else {
ignoreNamespaces = false;
}
for (String xPathPart : xPath.split(Constants.PATH_SEPARATOR)) {
if (Strings.isNullOrEmpty(xPathPart)) {
continue;
}
matchersByDepth.add(new ElementMatcherImpl(xPathPart, namespaces, ignoreNamespaces));
}
}
}
public MatchStatus isMatch(XMLEvent event) {
if (event.isStartElement()) {
depth++;
if (depth > matchersByDepth.size()) {
return MatchStatus.ELEMENT_NOT_MATCH;
} else if (depth-1 > matchesThroughDepth) {
return MatchStatus.ELEMENT_NOT_MATCH;
} else {
final ElementMatcher matcher = matchersByDepth.get(depth-1);
if (matcher.checkStartElement(event.asStartElement())) {
matchesThroughDepth = depth;
if (matchesThroughDepth == matchersByDepth.size()) {
// we have matched all levels through the current
return MatchStatus.ELEMENT_MATCH;
} else {
// at least one more level needs to match
return MatchStatus.UNDETERMINED;
}
} else {
// did not match at this level
return MatchStatus.ELEMENT_NOT_MATCH;
}
}
} else if (event.isEndElement()) {
depth--;
if (matchesThroughDepth > depth) {
matchesThroughDepth = depth;
}
return MatchStatus.UNDETERMINED;
} else {
return MatchStatus.UNDETERMINED;
}
}
}