[java] Properly handle enums with ignore identifiers

- Right now it throws an NPE
 - Having fixed the NPE, it threw an ArrayIndexOutOfBounds
 - Finally, it was just not working as expected
 - Fixes https://sourceforge.net/p/pmd/bugs/1542/
This commit is contained in:
Juan Martín Sotuyo Dodero
2016-11-09 18:13:27 -03:00
parent 938092cd14
commit a7b46723c8
3 changed files with 74 additions and 28 deletions

View File

@ -38,12 +38,7 @@ public class TokenEntry implements Comparable<TokenEntry> {
}
public TokenEntry(String image, String tokenSrcID, int beginLine) {
Integer i = TOKENS.get().get(image);
if (i == null) {
i = TOKENS.get().size() + 1;
TOKENS.get().put(image, i);
}
this.identifier = i.intValue();
setImage(image);
this.tokenSrcID = tokenSrcID;
this.beginLine = beginLine;
this.index = TOKEN_COUNT.get().getAndIncrement();
@ -128,4 +123,13 @@ public class TokenEntry implements Comparable<TokenEntry> {
}
return "--unkown--";
}
final void setImage(String image) {
Integer i = TOKENS.get().get(image);
if (i == null) {
i = TOKENS.get().size() + 1;
TOKENS.get().put(image, i);
}
this.identifier = i.intValue();
}
}

View File

@ -196,16 +196,16 @@ public class JavaTokenizer implements Tokenizer {
private static class ConstructorDetector {
private boolean ignoreIdentifiers;
private Deque<Integer> classMembersIndentations;
private Deque<TypeDeclaration> classMembersIndentations;
private int currentNestingLevel;
private boolean constructorCandidate;
private boolean storeNextIdentifier;
private String prevIdentifier;
public ConstructorDetector(boolean ignoreIdentifiers) {
this.ignoreIdentifiers = ignoreIdentifiers;
currentNestingLevel = 0;
classMembersIndentations = new LinkedList<Integer>();
classMembersIndentations = new LinkedList<TypeDeclaration>();
}
public void processToken(Token currentToken) {
@ -215,15 +215,21 @@ public class JavaTokenizer implements Tokenizer {
switch (currentToken.kind) {
case JavaParserConstants.IDENTIFIER:
// Could this be a constructor?
if (constructorCandidate && (!classMembersIndentations.isEmpty() && classMembersIndentations.peek().intValue() == currentNestingLevel)) {
prevIdentifier = currentToken.image;
if ("enum".equals(currentToken.image)) {
// If declaring an enum, add a new block nesting level at which constructors may exist
pushTypeDeclaration();
} else if (storeNextIdentifier) {
classMembersIndentations.peek().name = currentToken.image;
storeNextIdentifier = false;
}
// Store this token
prevIdentifier = currentToken.image;
break;
case JavaParserConstants.CLASS:
// If declaring a class, add a new block nesting level at which constructors may exist
classMembersIndentations.push(currentNestingLevel + 1);
pushTypeDeclaration();
break;
case JavaParserConstants.LBRACE:
@ -232,37 +238,44 @@ public class JavaTokenizer implements Tokenizer {
case JavaParserConstants.RBRACE:
// Discard completed blocks
if (classMembersIndentations.peek() == currentNestingLevel) {
if (!classMembersIndentations.isEmpty() &&
classMembersIndentations.peek().indentationLevel == currentNestingLevel) {
classMembersIndentations.pop();
}
currentNestingLevel--;
break;
}
// Can the next token be a constructor identifier?
constructorCandidate = currentToken.kind == JavaParserConstants.PRIVATE
|| currentToken.kind == JavaParserConstants.PROTECTED
|| currentToken.kind == JavaParserConstants.PUBLIC
|| currentToken.kind == JavaParserConstants.LBRACE
|| currentToken.kind == JavaParserConstants.RBRACE;
}
private void pushTypeDeclaration() {
TypeDeclaration cd = new TypeDeclaration(currentNestingLevel + 1);
classMembersIndentations.push(cd);
storeNextIdentifier = true;
}
public void restoreConstructorToken(Tokens tokenEntries, Token currentToken) {
if (!ignoreIdentifiers) {
return;
}
if (prevIdentifier != null) {
if (currentToken.kind == JavaParserConstants.LPAREN) {
// was the previous token a constructor? If so, restore the identifier
if (currentToken.kind == JavaParserConstants.LPAREN) {
if (!classMembersIndentations.isEmpty()
&& classMembersIndentations.peek().name.equals(prevIdentifier)) {
int lastTokenIndex = tokenEntries.size() - 1;
TokenEntry lastToken = tokenEntries.getTokens().get(lastTokenIndex);
tokenEntries.getTokens().set(lastTokenIndex,
new TokenEntry(prevIdentifier, lastToken.getTokenSrcID(), lastToken.getBeginLine()));
lastToken.setImage(prevIdentifier);
}
prevIdentifier = null;
}
}
}
private static class TypeDeclaration {
int indentationLevel;
String name;
public TypeDeclaration(int indentationLevel) {
this.indentationLevel = indentationLevel;
}
}
}

View File

@ -224,7 +224,7 @@ public class JavaTokensTokenizerTest {
"private static class Inner {" + PMD.EOL +
"Inner() { System.out.println(\"Guess who?\"); }" + PMD.EOL +
"}" + PMD.EOL +
"}" +PMD.EOL
"}" + PMD.EOL
));
Tokens tokens = new Tokens();
@ -243,6 +243,35 @@ public class JavaTokensTokenizerTest {
// Inner class constructor
assertEquals("Inner", tokenList.get(64).toString());
}
@Test
public void testIgnoreIdentifiersHandlesEnums() throws Throwable {
JavaTokenizer t = new JavaTokenizer();
t.setIgnoreAnnotations(false);
t.setIgnoreIdentifiers(true);
SourceCode sourceCode = new SourceCode(
new SourceCode.StringCodeLoader(
"package foo.bar.baz;" + PMD.EOL +
"public enum Foo {" + PMD.EOL +
"BAR(1)," + PMD.EOL +
"BAZ(2);" + PMD.EOL +
"Foo(int val) {" + PMD.EOL +
"}" + PMD.EOL +
"}" + PMD.EOL
));
Tokens tokens = new Tokens();
t.tokenize(sourceCode, tokens);
TokenEntry.getEOF();
List<TokenEntry> tokenList = tokens.getTokens();
// Enum member
assertEquals(String.valueOf(JavaParserConstants.IDENTIFIER), tokenList.get(4).toString());
assertEquals(String.valueOf(JavaParserConstants.IDENTIFIER), tokenList.get(9).toString());
// Enum constructor
assertEquals("Foo", tokenList.get(13).toString());
}
public static junit.framework.Test suite() {
return new junit.framework.JUnit4TestAdapter(JavaTokensTokenizerTest.class);