Каков наилучший способ сопоставления полного имени класса Java в тексте?
Примеры: java.lang.Reflect
, java.util.ArrayList
, org.hibernate.Hibernate
.
Каков наилучший способ сопоставления полного имени класса Java в тексте?
Примеры: java.lang.Reflect
, java.util.ArrayList
, org.hibernate.Hibernate
.
Полностью квалифицированное имя класса Java (скажем, "N" ) имеет структуру
N.N.N.N
Часть "N" должна быть идентификатором Java. Идентификаторы Java не могут начинаться с числа, но после начального символа они могут использовать любую комбинацию букв и цифр, подчеркивание или знаки доллара:
([a-zA-Z_$][a-zA-Z\d_$]*\.)*[a-zA-Z_$][a-zA-Z\d_$]*
------------------------ -----------------------
N N
Они также не могут быть зарезервированным словом (например, import
, true
или null
). Если вы хотите проверить правдоподобие, этого достаточно. Если вы также хотите проверить правильность, вы также должны проверить список зарезервированных слов.
Идентификаторы Java могут содержать любую букву Unicode вместо "только для латинского языка". Если вы хотите проверить это, используйте классы символов Unicode:
([\p{Letter}_$][\p{Letter}\p{Number}_$]*\.)*[\p{Letter}_$][\p{Letter}\p{Number}_$]*
или, если коротко
([\p{L}_$][\p{L}\p{N}_$]*\.)*[\p{L}_$][\p{L}\p{N}_$]*
Спецификация языка Java (раздел 3.8) содержит все сведения о действительных именах идентификаторов.
Также см. ответ на этот вопрос: Имена переменных Java Unicode
Вот полноценный рабочий класс с тестами, основанный на отличном комментарии @alan-moore
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.util.regex.Pattern;
import org.junit.Test;
public class ValidateJavaIdentifier {
private static final String ID_PATTERN = "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*";
private static final Pattern FQCN = Pattern.compile(ID_PATTERN + "(\\." + ID_PATTERN + ")*");
public static boolean validateJavaIdentifier(String identifier) {
return FQCN.matcher(identifier).matches();
}
@Test
public void testJavaIdentifier() throws Exception {
assertTrue(validateJavaIdentifier("C"));
assertTrue(validateJavaIdentifier("Cc"));
assertTrue(validateJavaIdentifier("b.C"));
assertTrue(validateJavaIdentifier("b.Cc"));
assertTrue(validateJavaIdentifier("aAa.b.Cc"));
assertTrue(validateJavaIdentifier("a.b.Cc"));
// after the initial character identifiers may use any combination of
// letters and digits, underscores or dollar signs
assertTrue(validateJavaIdentifier("a.b.C_c"));
assertTrue(validateJavaIdentifier("a.b.C$c"));
assertTrue(validateJavaIdentifier("a.b.C9"));
assertFalse("cannot start with a dot", validateJavaIdentifier(".C"));
assertFalse("cannot have two dots following each other",
validateJavaIdentifier("b..C"));
assertFalse("cannot start with a number ",
validateJavaIdentifier("b.9C"));
}
}
Образец, предоставленный Рено, работает. Но, насколько я могу судить, он всегда будет отступать в конце.
Чтобы оптимизировать его, вы можете поменять первую половину с последним. Обратите внимание на точечное совпадение, которое также необходимо изменить.
Ниже приведена моя версия, которая по сравнению с оригиналом работает примерно в два раза быстрее:
String ID_PATTERN = "\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*";
Pattern FQCN = Pattern.compile(ID_PATTERN + "(\\." + ID_PATTERN + ")*");
Я не могу писать комментарии, поэтому вместо этого я решил написать ответ.
Я пришел (сам по себе) к аналогичному ответу (как ответ Томалака), что-то вроде M.M.M.N:
([a-z][a-z_0-9]*\.)*[A-Z_]($[A-Z_]|[\w_])*
Где
M = ([a-z][a-z_0-9]*\.)*
N = [A-Z_]($[A-Z_]|[\w_])*
Однако это регулярное выражение (в отличие от ответа Томалака) дает больше предположений:
Имя пакета (часть M) будет только в нижнем регистре, первый символ M будет всегда нижней буквой, остальные могут смешать подчеркивание, нижние буквы и цифры.
Имя класса (часть N) всегда начинается с буквы верхнего регистра или символа подчеркивания, а остальные могут смешивать символы подчеркивания, буквы и цифры. Внутренние классы всегда начинаются с символа доллара ($) и должны подчиняться правилам имен классов, описанным ранее.
Примечание: шаблон \w является шаблоном XSD для букв и цифр (он не включает символ подчеркивания (_))
Надеюсь на эту помощь.
Следующее выражение прекрасно работает для меня.
^[a-z][a-z0-9_]*(\.[a-z0-9_]+)+$
Следующий класс проверяет правильность имени предоставленного пакета:
import java.util.HashSet;
public class ValidationUtils {
// All Java reserved words that must not be used in a valid package name.
private static final HashSet reserved;
static {
reserved = new HashSet();
reserved.add("abstract");reserved.add("assert");reserved.add("boolean");
reserved.add("break");reserved.add("byte");reserved.add("case");
reserved.add("catch");reserved.add("char");reserved.add("class");
reserved.add("const");reserved.add("continue");reserved.add("default");
reserved.add("do");reserved.add("double");reserved.add("else");
reserved.add("enum");reserved.add("extends");reserved.add("false");
reserved.add("final");reserved.add("finally");reserved.add("float");
reserved.add("for");reserved.add("if");reserved.add("goto");
reserved.add("implements");reserved.add("import");reserved.add("instanceof");
reserved.add("int");reserved.add("interface");reserved.add("long");
reserved.add("native");reserved.add("new");reserved.add("null");
reserved.add("package");reserved.add("private");reserved.add("protected");
reserved.add("public");reserved.add("return");reserved.add("short");
reserved.add("static");reserved.add("strictfp");reserved.add("super");
reserved.add("switch");reserved.add("synchronized");reserved.add("this");
reserved.add("throw");reserved.add("throws");reserved.add("transient");
reserved.add("true");reserved.add("try");reserved.add("void");
reserved.add("volatile");reserved.add("while");
}
/**
* Checks if the string that is provided is a valid Java package name (contains only
* [a-z,A-Z,_,$], every element is separated by a single '.' , an element can't be one of Java's
* reserved words.
*
* @param name The package name that needs to be validated.
* @return <b>true</b> if the package name is valid, <b>false</b> if its not valid.
*/
public static final boolean isValidPackageName(String name) {
String[] parts=name.split("\\.",-1);
for (String part:parts){
System.out.println(part);
if (reserved.contains(part)) return false;
if (!validPart(part)) return false;
}
return true;
}
/**
* Checks that a part (a word between dots) is a valid part to be used in a Java package name.
* @param part The part between dots (e.g. *PART*.*PART*.*PART*.*PART*).
* @return <b>true</b> if the part is valid, <b>false</b> if its not valid.
*/
private static boolean validPart(String part){
if (part==null || part.length()<1){
// Package part is null or empty !
return false;
}
if (Character.isJavaIdentifierStart(part.charAt(0))){
for (int i = 0; i < part.length(); i++){
char c = part.charAt(i);
if (!Character.isJavaIdentifierPart(c)){
// Package part contains invalid JavaIdentifier !
return false;
}
}
}else{
// Package part does not begin with a valid JavaIdentifier !
return false;
}
return true;
}
}
более короткая версия рабочего регулярного выражения:
\p{Alnum}[\p{Alnum}._]+\p{Alnum}
Я скажу что-то вроде ([\w]+\.)*[\w]+
Но, может быть, я могу быть более конкретным, зная, что вы хотите с ним делать;)