1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.github.tonywasher.joceanus.themis.parser;
18
19 import com.github.javaparser.JavaParser;
20 import com.github.javaparser.ParseResult;
21 import com.github.javaparser.ParserConfiguration;
22 import com.github.javaparser.Position;
23 import com.github.javaparser.Problem;
24 import com.github.javaparser.ast.CompilationUnit;
25 import com.github.javaparser.ast.Node;
26 import com.github.javaparser.ast.NodeList;
27 import com.github.javaparser.ast.PackageDeclaration;
28 import com.github.javaparser.ast.body.BodyDeclaration;
29 import com.github.javaparser.ast.expr.Expression;
30 import com.github.javaparser.ast.modules.ModuleDeclaration;
31 import com.github.javaparser.ast.stmt.Statement;
32 import com.github.javaparser.ast.type.Type;
33 import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
34 import io.github.tonywasher.joceanus.tethys.api.thread.TethysUIThreadStatusReport;
35 import io.github.tonywasher.joceanus.themis.exc.ThemisDataException;
36 import io.github.tonywasher.joceanus.themis.exc.ThemisIOException;
37 import io.github.tonywasher.joceanus.themis.parser.base.ThemisChar;
38 import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance;
39 import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisClassInstance;
40 import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisDeclarationInstance;
41 import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisExpressionInstance;
42 import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisModuleInstance;
43 import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisNodeInstance;
44 import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisStatementInstance;
45 import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisTypeInstance;
46 import io.github.tonywasher.joceanus.themis.parser.base.ThemisModifierList;
47 import io.github.tonywasher.joceanus.themis.parser.base.ThemisParserDef;
48 import io.github.tonywasher.joceanus.themis.parser.decl.ThemisDeclParser;
49 import io.github.tonywasher.joceanus.themis.parser.expr.ThemisExprParser;
50 import io.github.tonywasher.joceanus.themis.parser.mod.ThemisModModule;
51 import io.github.tonywasher.joceanus.themis.parser.mod.ThemisModParser;
52 import io.github.tonywasher.joceanus.themis.parser.node.ThemisNodeCompilationUnit;
53 import io.github.tonywasher.joceanus.themis.parser.node.ThemisNodeParser;
54 import io.github.tonywasher.joceanus.themis.parser.project.ThemisProject;
55 import io.github.tonywasher.joceanus.themis.parser.stmt.ThemisStmtParser;
56 import io.github.tonywasher.joceanus.themis.parser.type.ThemisTypeParser;
57
58 import java.io.File;
59 import java.io.FileInputStream;
60 import java.io.IOException;
61 import java.io.InputStream;
62 import java.nio.charset.StandardCharsets;
63 import java.util.ArrayDeque;
64 import java.util.ArrayList;
65 import java.util.Deque;
66 import java.util.List;
67
68
69
70
71 public class ThemisParser
72 implements ThemisParserDef {
73
74
75
76 private final TethysUIThreadStatusReport theReporter;
77
78
79
80
81 private final JavaParser theParser;
82
83
84
85
86 private final ThemisProject theProject;
87
88
89
90
91 private final Deque<ThemisInstance> theNodes;
92
93
94
95
96 private String thePackage;
97
98
99
100
101 private File theCurrentFile;
102
103
104
105
106 private final List<ThemisClassInstance> theClasses;
107
108
109
110
111 private final Deque<String> theClassStack;
112
113
114
115
116 private int theClassIndex;
117
118
119
120
121
122
123
124
125 public ThemisParser(final TethysUIThreadStatusReport pReport,
126 final File pLocation) throws OceanusException {
127
128 theReporter = pReport;
129 theParser = new JavaParser();
130 theNodes = new ArrayDeque<>();
131 theClassStack = new ArrayDeque<>();
132 theClasses = new ArrayList<>();
133
134
135 theProject = new ThemisProject(this, pLocation);
136
137
138 configureParser();
139
140
141 theProject.parseJavaCode();
142 }
143
144
145
146
147
148
149 public ThemisProject getProject() {
150 return theProject;
151 }
152
153 @Override
154 public TethysUIThreadStatusReport getReporter() {
155 return theReporter;
156 }
157
158 @Override
159 public List<ThemisClassInstance> getClasses() {
160 return theClasses;
161 }
162
163
164
165
166 private void configureParser() {
167
168 final ParserConfiguration myConfig = theParser.getParserConfiguration();
169 myConfig.setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_21);
170 }
171
172 @Override
173 public void setCurrentPackage(final String pPackage) {
174 thePackage = pPackage;
175 }
176
177 @Override
178 public void setCurrentFile(final File pFile) {
179 theCurrentFile = pFile;
180 theClasses.clear();
181 theClassStack.clear();
182 theClassIndex = 0;
183 }
184
185 @Override
186 public ThemisInstance registerInstance(final ThemisInstance pInstance) {
187
188 final ThemisInstance myParent = theNodes.peekLast();
189 if (myParent != null) {
190 myParent.registerChild(pInstance);
191 }
192
193
194 theNodes.addLast(pInstance);
195 return myParent;
196 }
197
198
199
200
201
202
203 private void deRegisterInstance(final ThemisInstance pInstance) {
204
205 if (pInstance != null) {
206
207 final ThemisInstance myCurrent = theNodes.peekLast();
208 if (pInstance.equals(myCurrent)) {
209
210 theNodes.removeLast();
211 }
212 if (pInstance instanceof ThemisClassInstance) {
213 theClassStack.removeLast();
214 }
215 }
216 }
217
218 @Override
219 public String registerClass(final ThemisClassInstance pClass) {
220
221 theClasses.add(pClass);
222
223
224 String myFullName = pClass.getFullName();
225 if (myFullName == null) {
226 final String myCurrentName = theClassStack.peekLast();
227 myFullName = myCurrentName
228 + ThemisChar.PERIOD
229 + ThemisChar.DOLLAR
230 + ++theClassIndex;
231 if (pClass.isLocalDeclaration()) {
232 myFullName += pClass.getName();
233 }
234 }
235
236
237 theClassStack.addLast(myFullName);
238
239
240 return myFullName;
241 }
242
243 @Override
244 public ThemisNodeCompilationUnit parseJavaFile() throws OceanusException {
245
246 try (InputStream myStream = new FileInputStream(theCurrentFile)) {
247
248 final ParseResult<CompilationUnit> myUnit = theParser.parse(myStream);
249 if (!myUnit.isSuccessful()) {
250 final Problem myProblem = myUnit.getProblem(0);
251 throw new ThemisDataException(myProblem.getVerboseMessage());
252 }
253 return (ThemisNodeCompilationUnit) parseNode(myUnit.getResult().orElse(null));
254
255
256 } catch (IOException e) {
257
258 throw new ThemisIOException("Failed to load file "
259 + theCurrentFile.getAbsolutePath(), e);
260 }
261 }
262
263 @Override
264 public ThemisModModule parseModuleInfo(final File pInfoFile) throws OceanusException {
265
266 setCurrentFile(pInfoFile);
267 try (InputStream myStream = new FileInputStream(theCurrentFile)) {
268
269 final String myText = new String(myStream.readAllBytes(), StandardCharsets.UTF_8);
270 final ParseResult<ModuleDeclaration> myDecl = theParser.parseModuleDeclaration(myText);
271 if (!myDecl.isSuccessful()) {
272 final Problem myProblem = myDecl.getProblem(0);
273 throw new ThemisDataException(myProblem.getVerboseMessage());
274 }
275 return (ThemisModModule) parseModule(myDecl.getResult().orElse(null));
276
277
278 } catch (IOException e) {
279
280 throw new ThemisIOException("Failed to load file "
281 + theCurrentFile.getAbsolutePath(), e);
282 }
283 }
284
285 @Override
286 public OceanusException buildException(final String pMessage,
287 final Node pNode) {
288
289 final Position myPos = pNode.getBegin().orElse(null);
290 final String myLocation = pNode.getClass().getCanonicalName()
291 + (myPos == null ? "" : ThemisChar.PARENTHESIS_OPEN
292 + myPos.line
293 + ThemisChar.COLON
294 + myPos.column
295 + ThemisChar.PARENTHESIS_CLOSE);
296
297
298 final String myMsg = pMessage
299 + ThemisChar.LF
300 + myLocation
301 + ThemisChar.LF
302 + theCurrentFile.getAbsolutePath();
303
304
305 return new ThemisDataException(myMsg);
306 }
307
308
309
310
311
312
313
314 public void checkPackage(final PackageDeclaration pPackage) throws OceanusException {
315
316 if (!thePackage.equals(pPackage.getNameAsString())) {
317 throw buildException("Mismatch on package", pPackage);
318 }
319 }
320
321 @Override
322 public ThemisDeclarationInstance parseDeclaration(final BodyDeclaration<?> pDecl) throws OceanusException {
323 final ThemisDeclarationInstance myInstance = ThemisDeclParser.parseDeclaration(this, pDecl);
324 deRegisterInstance(myInstance);
325 return myInstance;
326 }
327
328 @Override
329 public ThemisNodeInstance parseNode(final Node pNode) throws OceanusException {
330 final ThemisNodeInstance myInstance = ThemisNodeParser.parseNode(this, pNode);
331 deRegisterInstance(myInstance);
332 return myInstance;
333 }
334
335 @Override
336 public ThemisTypeInstance parseType(final Type pType) throws OceanusException {
337 final ThemisTypeInstance myInstance = ThemisTypeParser.parseType(this, pType);
338 deRegisterInstance(myInstance);
339 return myInstance;
340 }
341
342 @Override
343 public ThemisStatementInstance parseStatement(final Statement pStatement) throws OceanusException {
344 final ThemisStatementInstance myInstance = ThemisStmtParser.parseStatement(this, pStatement);
345 deRegisterInstance(myInstance);
346 return myInstance;
347 }
348
349 @Override
350 public ThemisExpressionInstance parseExpression(final Expression pExpr) throws OceanusException {
351 final ThemisExpressionInstance myInstance = ThemisExprParser.parseExpression(this, pExpr);
352 deRegisterInstance(myInstance);
353 return myInstance;
354 }
355
356 @Override
357 public ThemisModuleInstance parseModule(final Node pMod) throws OceanusException {
358 final ThemisModuleInstance myInstance = ThemisModParser.parseModule(this, pMod);
359 deRegisterInstance(myInstance);
360 return myInstance;
361 }
362
363 @Override
364 public ThemisModifierList parseModifierList(final NodeList<? extends Node> pNodeList) throws OceanusException {
365 return ThemisNodeParser.parseModifierList(this, pNodeList);
366 }
367 }