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 }