ParserContextImpl.java
/*
* 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.
*
*/
package org.esigate.parser;
import java.io.IOException;
import java.util.Stack;
import org.apache.http.HttpResponse;
import org.esigate.HttpErrorPage;
import org.esigate.impl.DriverRequest;
/**
*
* The stack of tags corresponding to the current position in the document
*
* @author Francois-Xavier Bonnet
*
*/
class ParserContextImpl implements ParserContext {
private final RootAdapter root;
private final DriverRequest httpRequest;
private final HttpResponse httpResponse;
private final Stack<ElementInfo> stack = new Stack<>();
ParserContextImpl(Appendable root, DriverRequest httpRequest, HttpResponse httpResponse) {
this.root = new RootAdapter(root);
this.httpRequest = httpRequest;
this.httpResponse = httpResponse;
}
@Override
public <T> T findAncestor(Class<T> type) {
T result = null;
for (int i = stack.size() - 1; i > -1; i--) {
Element currentElement = stack.elementAt(i).element;
if (type.isInstance(currentElement)) {
result = type.cast(currentElement);
break;
}
}
// try with root
if (result == null && type.isInstance(root.root)) {
result = type.cast(root.root);
}
return result;
}
/** {@inheritDoc} */
@Override
public boolean reportError(Exception e) {
boolean result = false;
for (int i = stack.size() - 1; i > -1; i--) {
Element element = stack.elementAt(i).element;
if (element.onError(e, this)) {
result = true;
break;
}
}
return result;
}
void startElement(ElementType type, Element element, String tag) throws IOException, HttpErrorPage {
boolean skipContent = false;
if (!stack.isEmpty()) {
// Inherit from parent
skipContent = stack.peek().skipContent;
}
boolean elementDoesNotSkipContent = element.onTagStart(tag, this);
if (!skipContent) {
skipContent = !elementDoesNotSkipContent;
}
stack.push(new ElementInfo(type, element, skipContent));
}
void endElement(String tag) throws IOException, HttpErrorPage {
ElementInfo elementInfo = stack.pop();
if (!elementInfo.skipContent) {
elementInfo.element.onTagEnd(tag, this);
}
}
boolean isCurrentTagEnd(String tag) {
return !stack.isEmpty() && stack.peek().type.isEndTag(tag);
}
/** Writes characters into current writer. */
public void characters(CharSequence cs) throws IOException {
characters(cs, 0, cs.length());
}
/** Writes characters into current writer. */
void characters(CharSequence csq, int start, int end) throws IOException {
getCurrent().characters(csq, start, end);
}
@Override
public Element getCurrent() {
Element result = root;
if (!stack.isEmpty()) {
result = stack.peek().element;
}
return result;
}
@Override
public DriverRequest getHttpRequest() {
return this.httpRequest;
}
private static class ElementInfo {
private final ElementType type;
private final Element element;
private final boolean skipContent;
public ElementInfo(ElementType type, Element element, boolean skipContent) {
this.type = type;
this.element = element;
this.skipContent = skipContent;
}
}
private static class RootAdapter implements Element {
private final Appendable root;
public RootAdapter(Appendable root) {
this.root = root;
}
@Override
public boolean onTagStart(String tag, ParserContext ctx) {
// Nothing to do, this is the root tag
return true;
}
@Override
public void onTagEnd(String tag, ParserContext ctx) {
// Nothing to do, this is the root tag
}
@Override
public boolean onError(Exception e, ParserContext ctx) {
return false;
}
@Override
public void characters(CharSequence csq, int start, int end) throws IOException {
this.root.append(csq, start, end);
}
}
@Override
public HttpResponse getHttpResponse() {
return this.httpResponse;
}
}