View Javadoc
1   /* 
2    * Licensed under the Apache License, Version 2.0 (the "License");
3    * you may not use this file except in compliance with the License.
4    * You may obtain a copy of the License at
5    *
6    * http://www.apache.org/licenses/LICENSE-2.0
7    *
8    * Unless required by applicable law or agreed to in writing, software
9    * distributed under the License is distributed on an "AS IS" BASIS,
10   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11   * See the License for the specific language governing permissions and
12   * limitations under the License.
13   *
14   */
15  
16  package org.esigate.parser;
17  
18  import java.io.IOException;
19  import java.util.Stack;
20  
21  import org.apache.http.HttpResponse;
22  import org.esigate.HttpErrorPage;
23  import org.esigate.impl.DriverRequest;
24  
25  /**
26   * 
27   * The stack of tags corresponding to the current position in the document
28   * 
29   * @author Francois-Xavier Bonnet
30   * 
31   */
32  class ParserContextImpl implements ParserContext {
33      private final RootAdapter root;
34      private final DriverRequest httpRequest;
35      private final HttpResponse httpResponse;
36  
37      private final Stack<ElementInfo> stack = new Stack<>();
38  
39      ParserContextImpl(Appendable root, DriverRequest httpRequest, HttpResponse httpResponse) {
40          this.root = new RootAdapter(root);
41          this.httpRequest = httpRequest;
42          this.httpResponse = httpResponse;
43      }
44  
45      @Override
46      public <T> T findAncestor(Class<T> type) {
47          T result = null;
48          for (int i = stack.size() - 1; i > -1; i--) {
49              Element currentElement = stack.elementAt(i).element;
50              if (type.isInstance(currentElement)) {
51                  result = type.cast(currentElement);
52                  break;
53              }
54          }
55          // try with root
56          if (result == null && type.isInstance(root.root)) {
57              result = type.cast(root.root);
58          }
59  
60          return result;
61      }
62  
63      /** {@inheritDoc} */
64      @Override
65      public boolean reportError(Exception e) {
66          boolean result = false;
67          for (int i = stack.size() - 1; i > -1; i--) {
68              Element element = stack.elementAt(i).element;
69              if (element.onError(e, this)) {
70                  result = true;
71                  break;
72              }
73          }
74          return result;
75      }
76  
77      void startElement(ElementType type, Element element, String tag) throws IOException, HttpErrorPage {
78          boolean skipContent = false;
79          if (!stack.isEmpty()) {
80              // Inherit from parent
81              skipContent = stack.peek().skipContent;
82          }
83          boolean elementDoesNotSkipContent = element.onTagStart(tag, this);
84          if (!skipContent) {
85              skipContent = !elementDoesNotSkipContent;
86          }
87          stack.push(new ElementInfo(type, element, skipContent));
88      }
89  
90      void endElement(String tag) throws IOException, HttpErrorPage {
91          ElementInfo elementInfo = stack.pop();
92          if (!elementInfo.skipContent) {
93              elementInfo.element.onTagEnd(tag, this);
94          }
95      }
96  
97      boolean isCurrentTagEnd(String tag) {
98          return !stack.isEmpty() && stack.peek().type.isEndTag(tag);
99      }
100 
101     /** Writes characters into current writer. */
102     public void characters(CharSequence cs) throws IOException {
103         characters(cs, 0, cs.length());
104     }
105 
106     /** Writes characters into current writer. */
107     void characters(CharSequence csq, int start, int end) throws IOException {
108         getCurrent().characters(csq, start, end);
109     }
110 
111     @Override
112     public Element getCurrent() {
113         Element result = root;
114         if (!stack.isEmpty()) {
115             result = stack.peek().element;
116         }
117         return result;
118     }
119 
120     @Override
121     public DriverRequest getHttpRequest() {
122         return this.httpRequest;
123     }
124 
125     private static class ElementInfo {
126         private final ElementType type;
127         private final Element element;
128         private final boolean skipContent;
129 
130         public ElementInfo(ElementType type, Element element, boolean skipContent) {
131             this.type = type;
132             this.element = element;
133             this.skipContent = skipContent;
134         }
135     }
136 
137     private static class RootAdapter implements Element {
138         private final Appendable root;
139 
140         public RootAdapter(Appendable root) {
141             this.root = root;
142         }
143 
144         @Override
145         public boolean onTagStart(String tag, ParserContext ctx) {
146             // Nothing to do, this is the root tag
147             return true;
148         }
149 
150         @Override
151         public void onTagEnd(String tag, ParserContext ctx) {
152             // Nothing to do, this is the root tag
153         }
154 
155         @Override
156         public boolean onError(Exception e, ParserContext ctx) {
157             return false;
158         }
159 
160         @Override
161         public void characters(CharSequence csq, int start, int end) throws IOException {
162             this.root.append(csq, start, end);
163         }
164 
165     }
166 
167     @Override
168     public HttpResponse getHttpResponse() {
169         return this.httpResponse;
170     }
171 }