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