Парсер для языка java

Tools for your Java code

Write code that can traverse Java source and look for the patterns you are interested in.

Transform

Build tools that can not just identify code patterns, but also has the ability to change them.

Generate

Be smart, don’t spend time writing boiler plate, generate it!

When choosing open source technologies it is important to know your choice will be rewarded by continuous support. The JavaParser community is vibrant and active, with a weekly release cadence that supports language features up to Java 12.

Business friendly license

Available either under either the terms of the LGPL or Apache Licenses.

An awesome community of approachable developers over on Gitter

Bring your source code to life

Our goal is to build a simple and lightweight set of tools to analyze, transform and generate Java code.

Quick Start

The JavaParser library provides you with an Abstract Syntax Tree of your Java code. The AST structure then allows you to work with your Java code in an easy programmatic way.

   com.github.javaparser javaparser-symbol-solver-core LATEST  
// Create a Java object representation of your code CompilationUnit compilationUnit = StaticJavaParser.parse("class A < >"); Optional classA = compilationUnit.getClassByName("A");
// Look for fields which are public and not static compilationUnit.findAll(FieldDeclaration.class).stream() .filter(f -> f.isPublic() && !f.isStatic()) .forEach(f -> System.out.println("Check field at line " + f.getRange().map(r -> r.begin.line).orElse(-1)));
// Ensure all abstract classes have a name starting with Abstract compilationUnit.findAll(ClassOrInterfaceDeclaration.class).stream() .filter(c -> !c.isInterface() && c.isAbstract() && !c.getNameAsString().startsWith("Abstract")) .forEach(c -> < String from = c.getNameAsString(); String to = "Abstract" + from; System.out.println("Renaming class " + from + " into " + to); c.setName(to); >);
// Create source code on the fly CompilationUnit compilationUnit = new CompilationUnit(); ClassOrInterfaceDeclaration myClass = compilationUnit .addClass("MyClass") .setPublic(true); myClass.addField(int.class, "A_CONSTANT", PUBLIC, STATIC); myClass.addField(String.class, "name", PRIVATE); String code = myClass.toString();

Join as Contributor

JavaParser is an open-source project built by amazing volunteers. We are always looking for new contributors and will support you throughout the process.

The best way to help the JavaParser community is to become a contributor; however if you would like to show your support in another way, please consider a purchase of our book.

JavaParser : Analyse, transform and generate your Java codebase

Источник

JavaParser. Корёжим код легко и непринуждённо

В мире существует множество клёвых маленьких библиотек, которые как бы и не знаменитые, но очень полезные. Идея в том, чтобы потихоньку знакомить Хабр с такими вещами. Сегодня расскажу о JavaParser.

JavaParser — это набор инструментов для парсинга, анализа, трансформации и генерации Java-кода. Иначе говоря, если нужно взять кусок джавакода и как-то его покорёжить подручными методами и без необходимости в особых знаниях, эта либа — самое то.

Где-то посреди статьи вы ВНЕЗАПНО можете осознать, какой кошмар и ужас можно сотворить этой либой, и никак не дождётесь дочитать текст и полить меня гневными комментариями. Не сдерживайтесь, не стоит — сразу скрольте до самого низу и изливайте душу 🙂

Код распространяется на гитхабе под лицензиями Apache, LGPL и GPL. Авторы сделали для проекта относительно приличный сайт, и даже запилили небольшую книжку, распространяемую совершенно бесплатно — что как бы подтверждает серьёзность их намерений.

Я перекинулся парой вопросов с авторами либы на FOSDEM, авторы оставляют впечатление умных и адекватных людей. Эта статья основана на их докладе.

Что делает эта либа? Вначале она превращает Java-код в AST (абстрактное синтаксическое дерево) — parsing. Во-вторых, она может взять уже готовый AST и превратить его в Java-код — unparsing.

В чем тут засада, и зачем нам вообще нужны библиотеки. Глядите:

String habraPostText = "Пыщ пыщ пыщ, ололо, я водитель НЛО"; public void writeHabraPost(String habraPostText) < habraPostText = "Привет, Хабр. Я сегодня пьян."; >public void writeHabraPost()

Для обоих этих методов AST в JavaParser сгенерится одинаковый. Конкретная нода в AST не знает, откуда именно пришел habraPostText .

Или например, у нас будут методы aMethod(int foo) и aMethod(String foo) , внутри которых переменная печатается с помощью System.out.println . AST тоже выйдет одинаковый.

Поэтому в JavaParser есть так называемый symbol solver , который каждому куску AST вычисляет конкретные соответствующие куски исходника. Он у них лежит в виде отдельного проекта на GitHub под названием JSS.

Существует код, который JavaParser может просчитать и без JSS. Например, если мы дёргаем у поля геттер, то ссылка на вызываемое поле закодирована прямо в код. С другой стороны, если есть метод, в котором как-то хитро вычисляется возвращаемый тип, то тут уже нужно подключать тяжелую артиллерию, коей является JSS. Вручную написать такую штуку было бы весьма сложно.

Теперь, зачем всё это может быть нужно. Например, вы хотите автоматизировать генерацию мусорного кода (привет, Lombok!). Или написать транспилер, который код на каком-то выдуманном вами скриптовом языке будет превращать в Java.

Код, который такое делает, очень простой. Давайте сделаем класс хабрапоста:

CompilationUnit cu = new CompilationUnit(); cu.setPackageDeclaration("ru.habrahabr.hub.java.examples.javaparser"); ClassOrInterfaceDeclaration habraPost = cu.addClass("HabraPost"); habraPost.addField("String", "title"); habraPost.addField("String", "text");

И теперь добавим конструктор, заставляющий заполнить эти поля:

habraPost.addConstructor(Modifier.PUBLIC) .addParameter("String", "title") .addParameter("String", "text") .setBody(new BlockStmt() .addStatement(new ExpressionStmt(new AssignExpr( new FieldAccessExpr(new ThisExpr(), "title"), new NameExpr("title"), AssignExpr.Operator.ASSIGN))) .addStatement(new ExpressionStmt(new AssignExpr( new FieldAccessExpr(new ThisExpr(), "text"), new NameExpr("text"), AssignExpr.Operator.ASSIGN)))); 

Теперь сгенерим бойлерплейт для геттеров-сеттеров:

habraPost.addMethod("getTitle", Modifier.PUBLIC).setBody( new BlockStmt().addStatement( new ReturnStmt(new NameExpr("title")))); habraPost.addMethod("getText", Modifier.PUBLIC).setBody( new BlockStmt().addStatement( new ReturnStmt(new NameExpr("text"))));

И теперь распечатаем наш класс в консоль:

System.out.println(cu.toString());

На выходе получится что-то вроде:

package ru.habrahabr.hub.java.examples.javaparser; public class HabraPost < String title; String text; public HabraPost(String title, String text) < this.title = title; this.text = text; >public void getTitle() < return title; >public void getText() < return text; >>

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

long wtfs = getNodes(myAPI, MethodDeclaration.class).stream() .filter(m -> m.getParameters().size > 10) .count(); System.out.println(String.format("Количество методов, за которые тебя стоит уволить: %d", wtfs));

Я не буду ударяться в сложные примеры, так как там всё видно по API. Единственная реально необычная фича, это то, что можно позвать JSS и покопаться в типах. Например, давайте найдём такой класс, который унаследован от максимального количества других классов (рекурсивно):

ResolvedReferenceTypeDeclaration c = getNodes(myAPI, ClassorInterfaceDeclaration).stream() .filter(c -> !c.isInterface()) .map(c -> c.resolve()) // JSS! .sorted(Comparator.comparingInt(o -> -1 * o.getAllAncestors().size))) .findFirst().get();

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

Допустим у нас есть метод checkMegamozg(Boolean moderatorInAGoodMood) , который Boomburum запускает у себя в мозгу 666 раз в день. Нужно превратить его в checkHabrahabr(Boolean moderatorInAGoodMood) .

Вначале мы ищем нужный метод:

getNodes(myAPI, MethodCallExpr.class).stream() .filter(m -> m.resolveInvokedMethod()). getQialifiedSignature() .equals("ru.habrahabr.Habr.checkMegamozg(java.lang.Boolean)")) .forEach(m -> m.replace(replaceCallsToMegamozg(m)));

Теперь как именно будет выглядеть замена:

public MethodCallExpr replaceCallsToMegamozg()

Причём если где-то там внутри методов затесались комментарии (привет, lany!), JavaParser всячески пытается их не потерять. Это жутко неприятная задача и авторы очень парятся об этой теме.

Как видим, эта либа — как маленький швейцарский ножик, простая и относительно надёжная. В будущем будут добавляться небольшие фичи типа встроенного языка шаблонов, чтобы можно было генерить Java-классы не вручную, а подгрузив их из файла и заменив $ .

Вродё всё. Ставьте лайки, подписывайтесь, и обязательно расскажите о том, что думаете об этой либе! Кроме того, можно заказать мне мини-обзор ещё какой-нибудь библиотеки — самые лучшие предложения превратятся в хабропосты.

Минутка рекламы. Как вы, наверное, знаете, мы делаем конференции. Ближайшие — JBreak 2018 и JPoint 2018. Можно туда прийти и вживую пообщаться с разработчиками разных моднейших технологий, например там будет Simon Ritter — Deputy CTO из Azul Systems. Там же можно встретиться c Виктором Гамовым из «Разбора Полётов» и кучей других интересных людей (и с бездельниками типа меня тоже можно пересечься). Короче, заходите, мы вас ждём.

Источник

Читайте также:  Проверка знака числа python
Оцените статью