Получение квалифицированного имени метода по номеру строки
Этот вопрос специфичен для Java и Maven. Обратите внимание на дополнительные ограничения, приведенные ниже, поскольку они отличаются от других вопросов.
У меня есть несколько проектов Maven (Java) для анализа. Что у меня есть:
- исходный код
- скомпилированный maven код Jave с двоичными файлами в target/ folder
Вопрос в том, что, учитывая один файл с исходным кодом (.java) и номер строки, как я могу получить полное имя метода, охватывающего эту строку? Если строка отсутствует в методе, просто выведите null. Приемлемые языки для реализации этого: Java, ruby или python.
Не могли бы вы ответить на вопрос одним из следующих двух способов?
используйте двоичный файл и извлеките квалифицированное имя метода этой строки. (Это может включать переплетение в отладочной информации, но это нормально.)
напрямую используйте указанный исходный файл, попробуйте разобрать его и использовать AST.
Использование определенных библиотек (например, BCEL) или любых сторонних (если они хорошо документированы и могут использоваться) также допустимо.
Большое спасибо за огромную помощь!
2 ответа
К сожалению, ваш вопрос полон недостатков:
- Конечно, вы можете анализировать входной источник (через анализатор Javacc или ANTLR), пока не достигнете нужной строки. Но кажется, что анализ одного и того же источника является пустой тратой усилий, поскольку у вас уже есть
.class
файлы. - Так что, кажется, лучше проанализировать
.class
файл. Но, к сожалению, у вас нет гарантии, что это класс, в котором создается ваша строка, потому что в одном и том же исходном файле может быть несколько классов.
Augh! Это приводит меня к некоему сложному решению:
Я объявлю класс, который будет содержать все логин:
public class SourceMethodsIndexer
{
private final SortedMap<Integer, List<Method>> indexOfMethodsByFirstLineNumber;
}
Конструктор будет выглядеть так:
public SourceMethodsIndexer(File sourceFile)
... и должен выполнить следующие задачи:
1. Найдите каталог класса, связанный с целевым пакетом.
File targetPackageDir=getTargetPackageDir(sourceFile);
File[] classFiles=targetPackageDir.listFiles(new FileFilter(){
public boolean accept(File dir, String name){
return name.endsWith(".class");
}
});
2. Используйте Apache BCEL для сбора всех непубличных классов, принадлежащих вашему исходному файлу (вы можете вызвать JavaClass.getSourceFileName()
фильтровать классы), плюс открытый класс, соответствующий имени вашего входного исходного файла.
Collection<JavaClass> targetClasses=getNonPublicClasses(classFiles, sourceFile.getName());
targetClasses.add(publicClass);
3. Соберите все методы в каждом классе.
Set<Method> targetMethods=new HashSet<Method>(1024);
for (JavaClass javaClass:targetClasses)
{
targetMethods.addAll(Arrays.asList(javaClass.getMethods()));
}
4.Теперь вы можете либо искать непосредственно по номеру вашей строки, либо сначала индексировать методы по номеру строки, чтобы позже получить к ним более быстрый доступ: JavaClass.getMethods()[n].getLineNumberTable().getSourceLine(0)
(позаботьтесь о том, чтобы могли быть повторные значения).
this.indexOfMethodsByFirstLineNumber=new TreeMap<Integer, List<Method>>((int)(1.7d*methods.size()));
for (Method method: methods)
{
// Note: The -1 in this line stands to make the SortedMap work properly when searching for ranges.
int firstLine=getLineNumberTable().getSourceLine(0)-1;
List<Method> methodsInTheSameLine=indexOfMethodsByFirstLineNumber.get(firstLine);
if (methodsInTheSameLine==null)
{
methodsInTheSameLine=new ArrayList<Method>();
indexOfMethodsByFirstLineNumber.put(firstLine,methodsInTheSameLine);
}
methodsInTheSameLine.add(method);
}
5. Опубликовать метод для поиска:
public Method getMethodByLine(int lineNumber)
{
Set<Method> methodsInTheSameLine=this.indexOfMethodsByFirstLineNumber.headMap(lineNumber).lastKey();
if (methodsInTheSameLine.size()==0)
{
// There are no methods method in that line: Absurd.
}
else if (methodsInTheSameLine.size()>1)
{
// There are more than one method in that line. Hardly probable, but possible.
}
else
{
// There is one method in that line:
return methods.get(0);
}
}
Существует ряд плагинов Maven с открытым исходным кодом, которые анализируют исходный код и отчитываются по каждому методу. Тщательное изучение некоторых из них может быть вашим лучшим выбором.
Примеры включают Checkstyle, FindBugs, PMD, JDepend, JavaNCSS.
Также взгляните на SonarQube.