Рефакторинг исходного кода Java на 7000 ссылок - PullRequest
21 голосов
/ 11 октября 2010

Мне нужно изменить сигнатуру метода, используемого во всей кодовой базе.

В частности, метод void log(String) будет принимать два дополнительных аргумента (Class c, String methodName), которые должны быть предоставлены вызывающей сторонойв зависимости от метода, где он вызывается.Я не могу просто передать null или аналогичный.

Чтобы дать представление об области действия, Eclipse обнаружил 7000 ссылок на этот метод, поэтому, если я изменю его, весь проект рухнет.Мне потребуется несколько недель, чтобы исправить это вручную.

Насколько я могу судить, плагин Eclipse для рефакторинга Eclipse не подходит для этой задачи, но я действительно хочу его автоматизировать.
Итак, как можноЯ выполнил работу?

Ответы [ 12 ]

22 голосов
/ 11 октября 2010

Отлично, я могу скопировать мой предыдущий ответ , и мне просто нужно отредактировать чуть-чуть:


Я думаю, что вам нужно сделать, это использовать источникдля этого необходимо выполнить синтаксический анализатор, например javaparser .

Для каждого исходного файла java проанализируйте его в CompilationUnit, создайте Visitor , вероятно, используя ModifierVisitorAdapter как базовый класс и переопределить (как минимум) visit(MethodCallExpr, arg).Затем запишите измененный CompilationUnit в новый файл и затем выполните diff.

Я бы не советовал менять исходный файл, но создание дерева теневых файлов может быть хорошей идеей (например, старый файл: src/main/java/com/mycompany/MyClass.java, новый файл src/main/refactored/com/mycompany/MyClass.java, таким образом вы можете различать все каталоги).

6 голосов
/ 11 октября 2010

Eclipse может сделать это, используя Refactor -> Изменить метод подписи и предоставить значения по умолчанию для новых параметров .

Дляпараметр класса defaultValue должен быть this.getClass (), но вы правы в своем комментарии. Я не знаю, как это сделать для параметра имени метода.

3 голосов
/ 11 октября 2010

IntelliJ IDEA не должно иметь проблем с этим.

Я не эксперт по Java, но что-то подобное может сработать. Это не идеальное решение (может быть, даже очень плохое), но оно может помочь вам начать:

Измените подпись метода с помощью инструментов рефакторинга IntelliJ и укажите значения по умолчанию для 2 новых параметров:

c: self.getClass()
methodName: Thread.currentThread().getStackTrace()[1].getMethodName()

или еще лучше, просто укажите null в качестве значений по умолчанию.

2 голосов
/ 11 октября 2010

Вам действительно нужно изменить код вызова и сигнатуру метода? Я имею в виду, что добавленные параметры предназначены для того, чтобы дать вам вызывающий класс и метод для добавления в ваши данные журнала. Если единственным требованием является просто добавление вызывающего класса / метода в данные журнала, тогда Thread.currentThread (). GetStackTrace () должен работать. Получив StackTraceElement [], вы можете получить имя класса и имя метода для вызывающей стороны.

2 голосов
/ 11 октября 2010

Может быть, я наивен, но почему вы не можете просто перегрузить имя метода?

void thing(paramA) {
    thing(paramA, THE_DEFAULT_B, THE_DEFAULT_C)
}

void thing(paramA, paramB, paramC) {
    // new method
}
2 голосов
/ 11 октября 2010

Я думаю, что есть несколько шагов, чтобы справиться с этим, поскольку это не просто техническая проблема, а «ситуация»:

  1. Отказаться делать это в короткие сроки из-за риска.
  2. Укажите проблемы, вызванные не использованием стандартных каркасов, а переизобретением колеса (как говорит Пол).
  3. Настаивайте на использовании Log4j или его эквивалента при внесении изменений.
  4. ИспользованиеРефакторинг Eclipse в разумных порциях для внесения изменений и работы с различными значениями по умолчанию.

Я использовал рефакторинг Eclipse для довольно больших изменений для исправления старого вонючего кода - в настоящее время он довольно устойчив.

1 голос
/ 12 октября 2010

Я согласен с ответом Seanizer о том, что вам нужен инструмент, который может анализировать Java. Это необходимо, но не достаточно; то, что вы действительно хотите, - это инструмент, который может провести надежное изменение массы.

Для этого вам понадобится инструмент, который может анализировать Java, сопоставлять шаблоны с проанализированным кодом, устанавливать вызов замены и выплевывать ответ, не разрушая остальную часть исходного кода.

Наш инструментарий реинжиниринга программного обеспечения DMS может делать все это для различных языков, включая Java. Он анализирует complete Java-системы исходного кода, создает абстрактные синтаксические деревья (для всего набора кода).

DMS может применять ориентированные по шаблону преобразования источник-источник для достижения желаемого изменения.

Для достижения эффекта ОП он применяет следующее преобразование программы :

 rule replace_legacy_log(s:STRING): expression -> expression
    " log(\s) " -> " log( \s, \class\(\), \method\(\) ) "

Это правило гласит: найдите вызов log, который имеет единственный строковый аргумент, и замените его вызовом log с еще двумя аргументами, определяемыми вспомогательными функциями class и метод .

Эти функции определяют содержащее имя метода и содержащее имя класса для корня узла AST, где правило находит совпадение.

Правило написано в «исходной форме», но фактически совпадает с AST и заменяет найденные AST измененными AST.

Чтобы вернуть измененный исходный код, вы просите DMS просто распечатать (чтобы сделать хороший макет) или распечатать верность (если вы хотите, чтобы макет старого кода был сохранен). DMS сохраняет комментарии, числа и т. Д. \

Если в существующем приложении имеется более одного определения функции «log», вам необходимо добавить классификатор:

... if IsDesiredLog().

где IsDesiredLog использует таблицу символов DMS и информацию о наследовании, чтобы определить, относится ли конкретный журнал к определению интереса.

1 голос
/ 11 октября 2010

Попробуйте рефакторинг, используя intellij.Он имеет функцию под названием SSR (Структурный поиск и замена).Вы можете ссылаться на классы, имена методов и т. Д. Для контекста.(ответ Сеанизатора более многообещающий, я проголосовал за него)

1 голос
/ 11 октября 2010

Если нужные вам строки попадают в небольшое количество категорий, то вам нужен Perl:

find -name '*.java' | xargs perl -pi -e 's/log\(([^,)]*?)\)/log(\1, "foo", "bar")/g'

Я предполагаю, что не было бы слишком сложно взломать скрипт, который бы в качестве второго аргумента поместил имя класса (полученное из имени файла) Получение имени метода в качестве третьего аргумента оставлено читателю в качестве упражнения.

0 голосов
/ 11 октября 2010

Если класс и имя метода требуются для "откуда взялся этот журнал?" введите данные, а другой вариант - распечатать трассировку стека в методе журнала. Э.Г.

public void log(String text)
{
   StringWriter sw = new StringWriter();
   PrintWriter pw = new PrintWriter(sw, true);
   new Throwable.printStackTrace(pw);
   pw.flush();
   sw.flush();
   String stackTraceAsLog = sw.toString();
   //do something with text and stackTraceAsLog
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...