Цель этой статьи — развить некоторые из моих работ с превосходным модулем детектора мертвого кода Джеретана Виленга для NetBeans.
Вы можете прочитать об этом здесь: https://blogs.oracle.com/geertjan/entry/dead_code_detection_for_all .
Плагин основан на библиотеке Dead Code Detector от Emeric Vernat. Эту библиотеку можно найти здесь: https://java.net/projects/dcd/pages/Home .
Когда я приближался к выпуску приложения на платформе NetBeans, над которым я работал, я понял, что мои модули, вероятно, были полны кода, который был заменен, устарел или просто не использовался. Отвечая на мой запрос на форуме NetBeans, Гертьян не только написал записи в блоге о библиотеке, но и создал для нее плагин.
Эта проблема
Источник для плагина Geertjan легко доступен здесь: https://java.net/projects/nb-api-samples/sources/api-samples/show/versions/7.3/misc/NB-DCD Плагин работает, собирая проект папки всех выбранных проектов и добавление их в каталоги для сканирования с помощью Dead Code Detector:
public final class DetectDeadCodeAction implements ActionListener {
private final List context;
public DetectDeadCodeAction(List<Project> context) {
this.context = context;
}
@Override
public void actionPerformed(ActionEvent ev) {
List directories = new ArrayList<File>();
for (Project project : context) {
directories.add(FileUtil.toFile(project.getProjectDirectory()));
}
// Call the Dead Code Detector with the list of directories.
}
}
Работая с проектом платформы NetBeans, я в основном заинтересован в двух видах проектов; модули и комплекты. Вышеупомянутый подход работает в большинстве случаев, когда все модули для пакета находятся в каталоге пакета:
- / SuiteA / ModuleA
- / SuiteA / moduleB
- / SuiteA / moduleC
При добавлении каталога / suiteA в каталоги для сканирования, каталоги для модулей A, B и C также сканируются.
Но что происходит, когда некоторые из ваших модулей находятся в другом каталоге?
- / SuiteA / MODULEX
- / Некоторые / другие / папки / moduleY
- / Пока / другой / папка / moduleZ
Простое добавление / suiteA подберет модуль X, но модули Y и Z не будут сканироваться. Наборы проектов NetBeans могут включать в себя модули, которые находятся в других местах, поэтому необходимо изменить действие, чтобы сделать несколько вещей. Во-первых, нам нужно определить, является ли проект пакетом. Если это пакет, нам нужно найти все модули для пакета и добавить их каталоги для сканирования.
Это люкс?
Возможно, есть и другие способы сделать это, но решение, которое я в итоге реализовал, смотрит на файл project.xml. Обычно можно предположить, что файл project.xml действителен и правильно сформирован, если пользователь может загрузить и выбрать проект в среде IDE.
Вот как выглядит файл project.xml:
<project xmlns="http://www.netbeans.org/ns/project/1"> <type>org.netbeans.modules.apisupport.project.suite</type> <configuration> <data xmlns="http://www.netbeans.org/ns/nb-module-suite-project/1"> <name>DISPLAY NAME OF SUITE</name> </data> </configuration> </project>
Поэтому, если тип проекта «org.netbeans.module.apisupport.project.suite», мы определили набор проектов.
public static final String SUITE = "<type>org.netbeans.modules.apisupport.project.suite</type>";
public boolean isProjectSuite(final Project project) {
boolean returnValue = false;
final FileObject projectDir = project.getProjectDirectory();
final FileObject projectXml = projectDir.getFileObject(
"nbproject/project.xml");
if (projectXml != null) {
try {
final InputStream inStream = projectXml.getInputStream();
final String xmlData = IOUtils.toString(inStream, "UTF-8");
IOUtils.closeQuietly(inStream);
returnValue = xmlData.contains(SUITE);
} catch (IOException ioEx) {
Exceptions.printStackTrace(ioEx);
}
}
return returnValue;
}
Код просто удостоверяется, что project.xml существует, считывает весь файл в строку (с помощью пакета ввода-вывода Apache Commons) и проверяет, что тип действительно является набором. В качестве альтернативы, мы могли бы загрузить его, используя библиотеки DOM, и искать элемент <type> через XPath или что-то еще, но в этом случае это казалось излишним.
Каталоги модулей для A Suite
Итак, как только проект определен как набор, как мы определяем, где каждый из модулей живет? Ответ лежит в файле project.properties:
modules=\
${project.org.netbeans.moduleA}:\
${project.org.netbeans.moduleB}:\
${project.org.netbeans.moduleC}
project.org.netbeans.moduleA=moduleA
project.org.netbeans.moduleB=moduleB
project.org.netbeans.moduleC=../../other/folder/moduleC
Таким образом, каждый из ключей свойств, начинающихся с «project», имеет значения, которые представляют относительный путь к каталогу этого проекта. Отлично!
private Set<File> getProjectsForSuite(final Project project) {
final Set<File> projectDirs = new HashSet<File>();
final FileObject suiteDir = project.getProjectDirectory();
final FileObject suiteProps = suiteDir.getFileObject(
"nbproject/project.properties");
if (suiteProps != null) {
try {
final InputStream inStream = suiteProps.getInputStream();
final Properties props = new Properties();
props.load(inStream);
IOUtils.closeQuietly(inStream);
for (String key : props.stringPropertyNames()) {
if (key.startsWith("project.")) {
final String relPath = props.getProperty(key);
final FileObject projectDir =
suiteDir.getFileObject(relPath);
if (projectDir != null) {
projectDirs.add(FileUtil.toFile(projectDir));
} else {
LOG.log(Level.INFO, "Null path for: {0}", relPath);
}
}
}
} catch (IOException ioEx) {
Exceptions.printStackTrace(ioEx);
}
}
return projectDirs;
}
В приведенном выше коде мы загружаем файл свойств в объект Properties. Мы перебираем все ключи и ищем те, которые начинаются с «проекта». Мы берем каталог набора и значение «проекта». ключи. FileObject имеет встроенную поддержку относительных путей, поэтому получение нового FileObject из каталога набора и значение ключа тривиально. Если каталог действительно существует, мы добавляем его в набор каталогов, возвращаемых методом.
Собираем все вместе
Вот модифицированная версия метода Geerjan actionPerformed:
public final class DetectDeadCodeAction implements ActionListener {
private final List<Project> context;
public DetectDeadCodeAction(List<Project> context) {
this.context = context;
}
@Override
public void actionPerformed(ActionEvent ev) {
List<File> directories = new ArrayList<File>();
for (Project project : context) {
if (isProjectSuite(project)) {
directories.addAll(getProjectsForSuite(project));
} else {
directories.add(FileUtil.toFile(project.getProjectDirectory()));
}
}
// Call the Dead Code Detector with the list of directories.
}
}
Идти дальше
Детектор мертвого кода не работает с исходными файлами. Это работает против скомпилированного кода; JAR-файлы и файлы классов. Возможно, что ваш каталог build / target может не существовать в той же иерархии каталогов, что и исходный каталог. Может быть случай использования для указания каталога build / target для проекта. Как Ant, так и Maven могут указывать расположение своих исходных каталогов и каталогов build / target, так что может быть случай, чтобы удовлетворить эти типы проектов. В настоящее время в проектах Ant и Maven обычно есть каталоги как source, так и build / target, расположенные в подкаталоге проекта, поэтому в большинстве случаев действие должно работать как есть.
Dead Code Detector — это увлекательный проект, который работает (и быстро), чтобы найти потенциально мертвый код в ваших проектах. Поздравляем Emeric за его работу над этим проектом и поздравляем Geertjan за быструю разработку плагина, который прекрасно интегрируется с IDE!