Цель этой статьи — развить некоторые из моих работ с превосходным модулем детектора мертвого кода Джеретана Виленга для 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!