'How to validate if a string would be a valid java variable?

How can I best check if a string input would be a valid java variable for coding? I'm sure I'm not the first one who is willing to do this. But maybe I'm missing the right keyword to find something useful.

Best would probably be a RegEx, which checks that:

  • starts with a letter

  • can then contain digits, letters

  • can contain some special characters, like '_' (which?)

  • may not contain a whitespace separator


Solution 1:[1]

Java 6+

Use

import javax.lang.model.SourceVersion;

boolean isValidVariableName(CharSequence name) {
    return SourceVersion.isIdentifier(name) && !SourceVersion.isKeyword(name);
}

if you need to check whether a string is a valid Java variable name in the latest version of Java or

import javax.lang.model.SourceVersion;

boolean isValidVariableNameInVersion(CharSequence name, SourceVersion version) {
    return SourceVersion.isIdentifier(name) && !SourceVersion.isKeyword(name, version);
}

if you need to check whether a string is a valid Java variable name in a specific Java version.

For example, underscore became a reserved keyword starting from Java 9, so isValidVariableNameInVersion("_", SourceVersion.RELEASE_9) returns false while isValidVariableNameInVersion("_", SourceVersion.RELEASE_8) returns true.

How it works

SourceVersion.isIdentifier(CharSequence name) checks whether or not name is a syntactically valid identifier (simple name) or keyword in the latest source version. !SourceVersion.isKeyword(name) returns false for keywords. As a result, SourceVersion.isIdentifier(name) && !SourceVersion.isKeyword(name) returns true for valid indetifiers and only for them.

The same approach is used in the built-in method SourceVersion.isName(CharSequence name, SourceVersion version) that checks whether name is a syntactically valid qualified name, which means that it will return true for strings like "apple.color":

public static boolean isName(CharSequence name, SourceVersion version) {
    String id = name.toString();

    for(String s : id.split("\\.", -1)) {
        if (!isIdentifier(s) || isKeyword(s, version))
            return false;
    }
    return true;
}

Test

import org.junit.jupiter.api.Test;

import javax.lang.model.SourceVersion;

import static org.assertj.core.api.Assertions.assertThat;

public class ValidVariableNameTest {
    boolean isValidVariableName(CharSequence name) {
        return isValidVariableNameInVersion(name, SourceVersion.RELEASE_8);
    }

    boolean isValidVariableNameInVersion(CharSequence name, SourceVersion version) {
        return SourceVersion.isIdentifier(name) && !SourceVersion.isKeyword(name, version);
    }

    @Test
    void variableNamesCanBeginWithLetters() {
        assertThat(isValidVariableName("test")).isTrue();
        assertThat(isValidVariableName("e2")).isTrue();
        assertThat(isValidVariableName("w")).isTrue();
        assertThat(isValidVariableName("??????")).isTrue();
    }

    @Test
    void variableNamesCanBeginWithDollarSign() {
        assertThat(isValidVariableName("$test")).isTrue();
        assertThat(isValidVariableName("$e2")).isTrue();
        assertThat(isValidVariableName("$w")).isTrue();
        assertThat(isValidVariableName("$??????")).isTrue();
        assertThat(isValidVariableName("$")).isTrue();
        assertThat(isValidVariableName("$55")).isTrue();
    }

    @Test
    void variableNamesCanBeginWithUnderscore() {
        assertThat(isValidVariableName("_test")).isTrue();
        assertThat(isValidVariableName("_e2")).isTrue();
        assertThat(isValidVariableName("_w")).isTrue();
        assertThat(isValidVariableName("_??????")).isTrue();
        assertThat(isValidVariableName("_55")).isTrue();
    }

    @Test
    void variableNamesCannotContainCharactersThatAreNotLettersOrDigits() {
        assertThat(isValidVariableName("apple.color")).isFalse();
        assertThat(isValidVariableName("my var")).isFalse();
        assertThat(isValidVariableName(" ")).isFalse();
        assertThat(isValidVariableName("apple%color")).isFalse();
        assertThat(isValidVariableName("apple,color")).isFalse();
        assertThat(isValidVariableName(",applecolor")).isFalse();
    }

    @Test
    void variableNamesCannotStartWithDigit() {
        assertThat(isValidVariableName("2e")).isFalse();
        assertThat(isValidVariableName("5")).isFalse();
        assertThat(isValidVariableName("123test")).isFalse();
    }


    @Test
    void differentSourceVersionsAreHandledCorrectly() {
        assertThat(isValidVariableNameInVersion("_", SourceVersion.RELEASE_9)).isFalse();
        assertThat(isValidVariableNameInVersion("_", SourceVersion.RELEASE_8)).isTrue();

        assertThat(isValidVariableNameInVersion("enum", SourceVersion.RELEASE_9)).isFalse();
        assertThat(isValidVariableNameInVersion("enum", SourceVersion.RELEASE_4)).isTrue();
    }

    @Test
    void keywordsCannotBeUsedAsVariableNames() {
        assertThat(isValidVariableName("strictfp")).isFalse();
        assertThat(isValidVariableName("assert")).isFalse();
        assertThat(isValidVariableName("enum")).isFalse();

        // Modifiers
        assertThat(isValidVariableName("public")).isFalse();
        assertThat(isValidVariableName("protected")).isFalse();
        assertThat(isValidVariableName("private")).isFalse();

        assertThat(isValidVariableName("abstract")).isFalse();
        assertThat(isValidVariableName("static")).isFalse();
        assertThat(isValidVariableName("final")).isFalse();

        assertThat(isValidVariableName("transient")).isFalse();
        assertThat(isValidVariableName("volatile")).isFalse();
        assertThat(isValidVariableName("synchronized")).isFalse();

        assertThat(isValidVariableName("native")).isFalse();

        // Declarations
        assertThat(isValidVariableName("class")).isFalse();
        assertThat(isValidVariableName("interface")).isFalse();
        assertThat(isValidVariableName("extends")).isFalse();
        assertThat(isValidVariableName("package")).isFalse();
        assertThat(isValidVariableName("throws")).isFalse();
        assertThat(isValidVariableName("implements")).isFalse();

        // Primitive types and void
        assertThat(isValidVariableName("boolean")).isFalse();
        assertThat(isValidVariableName("byte")).isFalse();
        assertThat(isValidVariableName("char")).isFalse();
        assertThat(isValidVariableName("short")).isFalse();
        assertThat(isValidVariableName("int")).isFalse();
        assertThat(isValidVariableName("long")).isFalse();
        assertThat(isValidVariableName("float")).isFalse();
        assertThat(isValidVariableName("double")).isFalse();
        assertThat(isValidVariableName("void")).isFalse();

        // Control flow
        assertThat(isValidVariableName("if")).isFalse();
        assertThat(isValidVariableName("else")).isFalse();

        assertThat(isValidVariableName("try")).isFalse();
        assertThat(isValidVariableName("catch")).isFalse();
        assertThat(isValidVariableName("finally")).isFalse();

        assertThat(isValidVariableName("do")).isFalse();
        assertThat(isValidVariableName("while")).isFalse();
        assertThat(isValidVariableName("for")).isFalse();
        assertThat(isValidVariableName("continue")).isFalse();

        assertThat(isValidVariableName("switch")).isFalse();
        assertThat(isValidVariableName("case")).isFalse();
        assertThat(isValidVariableName("default")).isFalse();
        assertThat(isValidVariableName("break")).isFalse();
        assertThat(isValidVariableName("throw")).isFalse();

        assertThat(isValidVariableName("return")).isFalse();

        // Other keywords
        assertThat(isValidVariableName("this")).isFalse();
        assertThat(isValidVariableName("new")).isFalse();
        assertThat(isValidVariableName("super")).isFalse();
        assertThat(isValidVariableName("import")).isFalse();
        assertThat(isValidVariableName("instanceof")).isFalse();

        // Reserved keywords
        assertThat(isValidVariableName("goto")).isFalse();
        assertThat(isValidVariableName("const")).isFalse();
    }

    @Test
    void literalsCannotBeUsedAsVariableNames() {
        assertThat(isValidVariableName("null")).isFalse();
        assertThat(isValidVariableName("true")).isFalse();
        assertThat(isValidVariableName("false")).isFalse();
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1