|
14 | 14 | import twg2.ast.interm.field.FieldSig;
|
15 | 15 | import twg2.ast.interm.method.MethodSigSimple;
|
16 | 16 | import twg2.ast.interm.type.TypeSig;
|
17 |
| -import twg2.collections.dataStructures.BaseList; |
18 | 17 | import twg2.collections.interfaces.ListReadOnly;
|
19 | 18 | import twg2.parser.codeParser.AstExtractor;
|
20 | 19 | import twg2.parser.codeParser.extractors.AccessModifierExtractor;
|
@@ -197,50 +196,60 @@ private static String parsePackageDeclaration(SimpleTree<CodeToken> astTree) {
|
197 | 196 | }
|
198 | 197 |
|
199 | 198 |
|
200 |
| - /** Reads backward from a '{' block through a simple class signature ({@code ClassName [extends ClassName] [implements InterfaceNme]}). |
| 199 | + /** Reads backward from a '{' block through a simple class signature ({@code ClassName [extends ClassName] [implements InterfaceName, InterfaceName, ...]}). |
201 | 200 | * Returns the iterator where {@code next()} would return the class name element.
|
202 |
| - * @return {@code <className, extendImplementNames>} |
| 201 | + * @return {@code <className, extendImplementNames[]>} |
203 | 202 | */
|
204 | 203 | private static Entry<String, List<String>> readClassIdentifierAndExtends(ListIterator<SimpleTree<CodeToken>> iter) {
|
205 |
| - var lang = CodeLanguageOptions.JAVA; |
206 |
| - var keywordUtil = lang.getKeywordUtil(); |
| 204 | + var keywordUtil = CodeLanguageOptions.JAVA.getKeywordUtil(); |
207 | 205 | // class signatures are read backward from the opening '{'
|
208 | 206 | int prevCount = 0;
|
209 | 207 | var names = new ArrayList<String>();
|
| 208 | + boolean lastNodeWasInheritanceKeyword = false; |
210 | 209 | Entry<String, List<String>> nameCompoundRes = null;
|
211 | 210 |
|
212 |
| - // get the first element and begin checking |
| 211 | + // get the node and begin checking backward |
213 | 212 | if(iter.hasPrevious()) { prevCount++; }
|
214 | 213 | SimpleTree<CodeToken> prevNode = iter.hasPrevious() ? iter.previous() : null;
|
215 | 214 |
|
216 |
| - while(prevNode != null && AstFragType.isIdentifierOrKeyword(prevNode.getData()) && !keywordUtil.blockModifiers().is(prevNode.getData()) && !keywordUtil.isInheritanceKeyword(prevNode.getData().getText())) { |
217 |
| - names.add(prevNode.getData().getText()); |
218 |
| - prevNode = iter.hasPrevious() ? iter.previous() : null; |
219 |
| - if(iter.hasPrevious()) { prevCount++; } |
220 |
| - } |
221 |
| - |
222 |
| - // if the class signature extends/implements, then the identifiers just read are the class/interface names, next read the actual class name |
223 |
| - if(prevNode != null && lang.getAstUtil().isKeyword(prevNode.getData(), JavaKeyword.EXTENDS, JavaKeyword.IMPLEMENTS)) { |
224 |
| - prevNode = iter.hasPrevious() ? iter.previous() : null; |
225 |
| - if(iter.hasPrevious()) { prevCount++; } |
226 |
| - if(prevNode != null && AstFragType.isIdentifierOrKeyword(prevNode.getData()) && !keywordUtil.blockModifiers().is(prevNode.getData())) { |
227 |
| - Collections.reverse(names); |
228 |
| - var extendImplementNames = names; |
229 |
| - String className = prevNode.getData().getText(); |
230 |
| - nameCompoundRes = Tuples.of(className, extendImplementNames); |
| 215 | + // read all identifiers until a block modifier is reached, including inheritance keywords (i.e. 'extends' and 'implements') |
| 216 | + // this creates a backward list of 'implements' class names, then 'extends' class names, then the actual class name |
| 217 | + while(prevNode != null && AstFragType.isIdentifierOrKeyword(prevNode.getData()) && !keywordUtil.blockModifiers().is(prevNode.getData())) { |
| 218 | + if(!keywordUtil.isInheritanceKeyword(prevNode.getData().getText())) { |
| 219 | + names.add(prevNode.getData().getText()); |
| 220 | + lastNodeWasInheritanceKeyword = false; |
231 | 221 | }
|
| 222 | + // basic syntax checking to make sure there's an identifier between 'extends'/'implements' keywords |
232 | 223 | else {
|
233 |
| - throw new IllegalStateException("found block with extend/implement names, but no class name " + names); |
| 224 | + if(lastNodeWasInheritanceKeyword) { |
| 225 | + throw new IllegalStateException("found two adjacent 'extends'/'implements' keywords with no intermediate class name"); |
| 226 | + } |
| 227 | + lastNodeWasInheritanceKeyword = true; |
234 | 228 | }
|
| 229 | + |
| 230 | + if(iter.hasPrevious()) { prevCount++; } |
| 231 | + prevNode = iter.hasPrevious() ? iter.previous() : null; |
| 232 | + } |
| 233 | + |
| 234 | + // syntax check, ensure there's a class name identifier between the block modifier and 'extends'/'implements' keyword |
| 235 | + if(lastNodeWasInheritanceKeyword) { |
| 236 | + throw new IllegalStateException("found 'extends'/'implements' keyword but no class name"); |
| 237 | + } |
| 238 | + |
| 239 | + // move iterator forward since the while loop reads past the class name to the first block modifier |
| 240 | + if(prevCount > 0) { |
| 241 | + iter.next(); |
235 | 242 | }
|
236 |
| - // else, we should have only read one name with the loop and it is the class name |
237 |
| - else if(names.size() == 1) { |
238 |
| - String className = names.get(0); |
239 |
| - nameCompoundRes = Tuples.of(className, Collections.emptyList()); |
240 |
| - // move iterator forward since the while loop doesn't use the last value (i.e. reads one element past the valid elements it wants to consume) |
241 |
| - if(prevCount > 0) { |
242 |
| - iter.next(); |
| 243 | + |
| 244 | + // if a likely valid class block modifier has been reached, then the identifiers just read are the class/interface names and the class name |
| 245 | + if(prevNode != null && keywordUtil.blockModifiers().is(prevNode.getData())) { |
| 246 | + if(names.size() == 0) { |
| 247 | + throw new IllegalStateException("found block with no name"); |
243 | 248 | }
|
| 249 | + String className = names.remove(names.size() - 1); |
| 250 | + if(names.size() > 1) { Collections.reverse(names); } |
| 251 | + var extendImplementNames = names; |
| 252 | + nameCompoundRes = Tuples.of(className, extendImplementNames); |
244 | 253 | }
|
245 | 254 |
|
246 | 255 | return nameCompoundRes;
|
|
0 commit comments