1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.devacfr.maven.skins.reflow.snippet;
20
21 import javax.annotation.Nonnull;
22 import javax.annotation.Nullable;
23
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.Iterator;
27
28 import org.devacfr.maven.skins.reflow.ISkinConfig;
29 import org.devacfr.maven.skins.reflow.snippet.Processor.ShortcodeProcessor;
30 import org.devacfr.maven.skins.reflow.snippet.Processor.WebComponentProcessor;
31 import org.jsoup.Jsoup;
32 import org.jsoup.nodes.Document;
33 import org.jsoup.nodes.Element;
34 import org.jsoup.select.Elements;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 import static java.util.Objects.requireNonNull;
39
40
41
42
43
44 public class SnippetParser {
45
46 private static final Logger LOGGER = LoggerFactory.getLogger(SnippetParser.class);
47
48
49 private final ComponentResolver resolver;
50
51
52 private final ArrayList<ComponentToken> stack;
53
54
55 private Iterator<Element> it;
56
57
58 private Processor state = null;
59
60
61 private final WebComponentProcessor webComponentProcessor;
62
63
64 private final ShortcodeProcessor shortcodeProcessor;
65
66
67 private final SnippetContext snippetContext;
68
69
70 private ComponentToken currentToken;
71
72 public SnippetParser() {
73 resolver = new ComponentResolver();
74 stack = new ArrayList<>(32);
75 snippetContext = new SnippetContext(this);
76 webComponentProcessor = new WebComponentProcessor(this);
77 shortcodeProcessor = new ShortcodeProcessor(this);
78 }
79
80 public SnippetParser insertResourcePath(final String path, final int index) {
81 snippetContext.insertResourcePath(path, index);
82 return this;
83 }
84
85 public SnippetParser addResourcePath(final String path) {
86 snippetContext.addResourcePath(path);
87 return this;
88 }
89
90 public SnippetContext parse(@Nonnull final ISkinConfig config, @Nullable String htmlSource)
91 throws IOException {
92 requireNonNull(config);
93 if (htmlSource == null) {
94 htmlSource = "";
95 }
96
97 snippetContext.reset();
98 snippetContext.setConfig(config);
99
100 if (LOGGER.isDebugEnabled()) {
101 LOGGER.debug("Parse Snippet");
102 LOGGER.debug(htmlSource);
103 }
104
105
106 final Document doc = resolver.normalize(Jsoup.parse(htmlSource));
107
108 final Elements elements = resolver.collect(doc);
109
110 for (it = elements.iterator(); it.hasNext();) {
111 try {
112 parse();
113 } catch (final Exception ex) {
114 throw new SnippetParseException(
115 "error on parse token " + currentToken + " when generate file " + config.getFileId(), ex);
116 }
117 }
118 snippetContext.setHtmlSource(doc.html());
119 return snippetContext;
120 }
121
122 protected void parse() {
123 if (!it.hasNext()) {
124 throw new SnippetParseException("EOF");
125 }
126 final Element element = it.next();
127 currentToken = resolver.create(element);
128 if (currentToken == null) {
129 throw new SnippetParseException("unknown component: " + element);
130 }
131 switch (currentToken.type()) {
132 case webComponent:
133 state = webComponentProcessor;
134 break;
135 case shortcode:
136 state = shortcodeProcessor;
137 break;
138 default:
139 throw new SnippetParseException("unknown token type " + currentToken.type());
140 }
141 parse(currentToken);
142 currentToken = null;
143 }
144
145 protected void parse(final ComponentToken token) {
146 state.parse(token);
147 }
148
149 protected ComponentToken currentToken() {
150 final int size = stack.size();
151 return size > 0 ? stack.get(size - 1) : null;
152 }
153
154 protected ComponentToken pop() {
155 final int size = stack.size();
156 if (LOGGER.isDebugEnabled()) {
157 LOGGER.debug("Stack size befor pop: {}", size);
158 }
159 final ComponentToken element = stack.remove(size - 1);
160 if (LOGGER.isDebugEnabled()) {
161 LOGGER.debug("Remove component from stack: {}", element);
162 }
163 return element;
164 }
165
166 protected void push(final ComponentToken element) {
167 stack.add(element);
168 if (LOGGER.isDebugEnabled()) {
169 LOGGER.debug("Add component to stack: {}", element);
170 }
171 }
172
173 protected SnippetContext getSnippetContext() {
174 return snippetContext;
175 }
176 }