Подтвердить что ты не робот

Рефакторинг источника Java из 7000 ссылок

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

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

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

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

4b9b3361

Ответ 1

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


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

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

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

Ответ 2

Eclipse может это сделать, используя Рефактор → Изменить подпись метода и предоставить значения по умолчанию для новых параметров.

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

Ответ 3

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

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

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

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

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

Ответ 4

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

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

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

Ответ 5

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

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

void thing(paramA, paramB, paramC) {
    // new method
}

Ответ 6

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

Ответ 7

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

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

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

Ответ 8

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

Ответ 9

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

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

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

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

Чтобы достичь эффекта OP, он применил бы следующее преобразование программы:

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

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

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

Правило написано в "исходной форме", но фактически соответствует АСТ и заменяет найденные АСТ модифицированным АСТ.

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

Если приложение exisitng имеет более одной функции "log", вам нужно добавить квалификатор:

... if IsDesiredLog().

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

Ответ 10

На самом деле ваша проблема заключается не в том, чтобы использовать движок click'n'play, который позволит вам заменить все вхождения

log("some weird message");

по

log(this.getClass(), new Exception().getStackTrace()[1].getMethodName());

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

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

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

Ответ 11

Я бы выполнил поиск и заменил log( на log(@class, @methodname,

Затем напишите немного script на любом языке (даже java), чтобы найти имя класса и имена методов и заменить токены @class и @method...

Удачи.

Ответ 12

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

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
}