|
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