FutureParserContextImpl.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.future;

import java.io.IOException;
import java.util.Map;
import java.util.Stack;
import java.util.concurrent.Future;

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
 * <p>
 * This class is based on ParserContextImpl
 * 
 * @see org.esigate.parser.ParserContextImpl
 * 
 * 
 * @author Nicolas Richeton
 * 
 */
class FutureParserContextImpl implements FutureParserContext {
    private final RootAdapter root;
    private final DriverRequest httpRequest;
    private final HttpResponse httpResponse;

    private final Stack<Pair> stack = new Stack<>();
    private Map<String, Object> data;

    FutureParserContextImpl(FutureAppendable root, DriverRequest httpRequest, HttpResponse httpResponse,
            Map<String, Object> data) {
        this.root = new RootAdapter(root);
        this.httpRequest = httpRequest;
        this.httpResponse = httpResponse;
        this.data = data;
    }

    @Override
    public <T> T findAncestor(Class<T> type) {
        T result = null;
        for (int i = stack.size() - 1; i > -1; i--) {
            FutureElement 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(FutureElement el, Exception e) {
        boolean result = false;
        FutureElement current = el.getParent();
        while (current != null) {
            if (current.onError(e, this)) {
                result = true;
                break;
            }
            current = current.getParent();
        }

        return result;
    }

    void startElement(FutureElementType type, FutureElement element, String tag) throws IOException, HttpErrorPage {
        element.onTagStart(tag, this);
        stack.push(new Pair(type, element));
    }

    void endElement(String tag) throws IOException, HttpErrorPage {
        FutureElement element = stack.pop().element;
        element.onTagEnd(tag, this);
    }

    boolean isCurrentTagEnd(String tag) {
        return !stack.isEmpty() && stack.peek().type.isEndTag(tag);
    }

    /** Writes characters into current writer. */
    public void characters(Future<CharSequence> csq) throws IOException {
        getCurrent().characters(csq);
    }

    @Override
    public FutureElement getCurrent() {
        FutureElement result = root;
        if (!stack.isEmpty()) {
            result = stack.peek().element;
        }
        return result;
    }

    @Override
    public DriverRequest getHttpRequest() {
        return this.httpRequest;
    }

    private static class Pair {
        private final FutureElementType type;
        private final FutureElement element;

        public Pair(FutureElementType type2, FutureElement element) {
            this.type = type2;
            this.element = element;
        }
    }

    private static class RootAdapter implements FutureElement {
        private final FutureAppendable root;

        public RootAdapter(FutureAppendable root) {
            this.root = root;
        }

        @Override
        public boolean onTagStart(String tag, FutureParserContext ctx) {
            // Nothing to do, this is the root tag
            return true;
        }

        @Override
        public void onTagEnd(String tag, FutureParserContext ctx) {
            // Nothing to do, this is the root tag
        }

        @Override
        public boolean onError(Exception e, FutureParserContext ctx) {
            return false;
        }

        @Override
        public void characters(Future<CharSequence> csq) throws IOException {
            this.root.enqueueAppend(csq);
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.esigate.parser.future.FutureElement#getParent()
         */
        @Override
        public FutureElement getParent() {
            // Root parser has no parent.
            return null;
        }
    }

    @Override
    public HttpResponse getHttpResponse() {
        return this.httpResponse;
    }

    @Override
    public Object getData(String key) {
        Object result = null;
        if (this.data != null) {
            result = this.data.get(key);
        }
        return result;
    }
}