Статьи

Эффективная Java: инструмент для изучения и измерения вашего Java-кода, написанного на Clojure

Когда я работал в TripAdvisor, у нас был внутренний книжный клуб. Мы начали с чтения  Effective Java , этой очень известной книги Джошуа Блоха. Интересно , и все , но я думаю , что эта книга была настолько успешной , что большинство из его советов в настоящее время является частью культуры Java: большинство разработчиков Java знает их и  вроде  применять их. Дело в том, что эти советы, хотя и очень разумные, не всегда применяются в больших кодовых базах. По этой причине я решил, что хочу создать инструмент для проверки того, что наша кодовая база (которая была… большой) действительно перенимала предложения из книги, поэтому я начал писать инструмент под названием «  Эффективная Java»  и начал писать его в Clojure.

Выполнение запросов из командной строки

Основная идея заключается в том, что вы можете выполнить несколько запросов к вашему коду. Выполненные запросы в основном основаны на советах книги. Например, один очевидный совет заключается в том, что вы не должны иметь тонны конструкторов и использовать вместо них фабричные методы. Итак, давайте предположим, что мы хотим проверить, какие классы имеют 5 или более конструкторов; вы запускаете это:

java -jar effectivejava-0.1.3-SNAPSHOT-standalone.jar -q mc -d "<myJavaProjectDir>" -t 5

И вы получите что-то вроде этого:

Considering 109 Java files
japa.parser.ast.expr.ArrayCreationExpr  :  5
japa.parser.ast.body.MethodDeclaration  :  5
japa.parser.ast.body.BaseParameter  :  5
japa.parser.ast.body.FieldDeclaration  :  5

На этом этапе вы можете посмотреть на этот код и решить, какие части следует реорганизовать для улучшения качества вашего кода. Я думаю, что единственный способ, который работает при работе с большой кодовой базой, — это последовательно улучшать ее с бесконечным терпением и любовью. Я нахожу очень полезным иметь какой-то способ сузить фокус на чем-то действенном (например, один единственный класс или один единственный метод), потому что если вы остановитесь и посмотрите на всю кодовую базу, вы просто уйдете в отчаяние и станете монахом где-то далеко-далеко от классов 10K строк или конструкторов, принимающих 20 параметров. Столкнувшись с такой сложной задачей, вы не должны думать, вы должны вместо этого найти одну проблему и решить ее. Один из способов сосредоточиться на том, чтобы кто-то нашел проблемы для вас.

paraocchi

Пусть инструмент будет твоим шутником.

Запросы реализованы

Количество реализованных в настоящее время запросов очень ограничено:

  • количество не частных конструкторов
  • количество аргументов для не приватных конструкторов
  • тип синглтона, реализуемый классом (открытое поле, статическая фабрика, перечисление)

Количество запросов ограничено, потому что я сосредоточился больше на создании нескольких способов использования инструмента (перечисленных ниже) и потому, что я работаю над слишком многими вещами :)

Как строится модель

Другая причина ограниченного количества запросов заключается в том, что в настоящее время я использую  JavaParser  для построения модели анализируемого кода. Хотя это отличный инструмент (не говоря об этом, потому что я участвую в проекте …: D), он не может разрешать символы. С одной стороны, это означает меньшую конфигурацию для пользователя, но ограничивает вид анализа, который возможно выполнить. Ситуация может измениться в будущем, потому что JavaParser развивается для поддержки такого анализа. Я также мог бы использовать другие инструменты, такие как  JaMoPP  или  MoDisco. , Такие инструменты, безусловно, являются более сложными и менее легкими, но, возможно, стоит присмотреться к ним поближе. В идеале нам нужен инструмент, который одновременно анализирует исходный код и способен построить модель для скомпилированного кода (с учетом наших зависимостей). Такие инструменты должны быть в состоянии интегрировать различные типы моделей, выполняющих анализ на полученной мегамодели. Таким образом, модель, полученная путем анализа файла Java, может иметь ссылки на модель, полученную путем анализа Jar-файла. Это такие вещи, которые требуют некоторой работы, а создание парсера — это лишь малая часть усилий.

Интерактивный режим

Я думаю, что в некоторых случаях вы не совсем то, что вы ищете. Вы хотите просто исследовать код и разбираться с вещами по ходу дела. Таким образом, вы можете проанализировать некоторый исходный код, задать несколько запросов, а затем изменить пороговые значения, чтобы ограничить свои результаты, и задать дополнительные запросы. Затем вы что-то исправляете и снова запускаете запрос. Что-то вроде этого:

Снимок экрана от 2015-04-05 11:04:15

Поэтому я потратил некоторое время на реализацию интерактивного режима вместо того, чтобы писать больше запросов. Глупо, но я должен сказать, что было интересно написать этот фрагмент кода Clojure. : D

Использование эффективной Java в качестве линтера для Java

Эта функция все еще находится на самой ранней стадии. В настоящее время выполняется несколько запросов на количество конструкторов и количество параметров для каждого конструктора.

lein run -l                                                                                                                                                                                                    ⏎
 [info]  Linter, no directory indicated. Using current directory
Loaded 440 files
japa.parser.ast.stmt.CatchClause : This class has too many constructors (4). Consider using static factory methods or the Builder pattern
japa.parser.ast.stmt.AssertStmt : This class has too many constructors (4). Consider using static factory methods or the Builder pattern
japa.parser.ast.body.AnnotationMemberDeclaration : This class has too many constructors (4). Consider using static factory methods or the Builder pattern
japa.parser.ast.body.EnumDeclaration : This class has too many constructors (4). Consider using static factory methods or the Builder pattern
japa.parser.ast.body.Parameter : This class has too many constructors (4). Consider using static factory methods or the Builder pattern
japa.parser.ast.body.EnumConstantDeclaration : This class has too many constructors (4). Consider using static factory methods or the Builder pattern
japa.parser.ast.body.VariableDeclarator : This class has too many constructors (4). Consider using static factory methods or the Builder pattern
japa.parser.ast.body.TypeDeclaration : This class has too many constructors (4). Consider using static factory methods or the Builder pattern
japa.parser.ast.body.BaseParameter : This class has too many constructors (5). Consider using static factory methods or the Builder pattern
japa.parser.ast.body.AnnotationDeclaration : This class has too many constructors (4). Consider using static factory methods or the Builder pattern
japa.parser.ast.body.MethodDeclaration : This class has too many constructors (5). Consider using static factory methods or the Builder pattern
japa.parser.ast.body.FieldDeclaration : This class has too many constructors (5). Consider using static factory methods or the Builder pattern
japa.parser.ast.body.ConstructorDeclaration : This class has too many constructors (4). Consider using static factory methods or the Builder pattern
japa.parser.ast.body.ClassOrInterfaceDeclaration : This class has too many constructors (4). Consider using static factory methods or the Builder pattern
japa.parser.ast.type.ClassOrInterfaceType : This class has too many constructors (4). Consider using static factory methods or the Builder pattern
japa.parser.ast.type.WildcardType : This class has too many constructors (4). Consider using static factory methods or the Builder pattern
japa.parser.ast.type.ReferenceType : This class has too many constructors (4). Consider using static factory methods or the Builder pattern
japa.parser.ast.PackageDeclaration : This class has too many constructors (4). Consider using static factory methods or the Builder pattern
japa.parser.ast.expr.ArrayCreationExpr : This class has too many constructors (5). Consider using static factory methods or the Builder pattern
japa.parser.ast.expr.VariableDeclarationExpr : This class has too many constructors (4). Consider using static factory methods or the Builder pattern
japa.parser.ast.expr.MethodCallExpr : This class has too many constructors (4). Consider using static factory methods or the Builder pattern
org.springframework.jdbc.object.BatchSqlUpdate : This class has too many constructors (4). Consider using static factory methods or the Builder pattern
org.springframework.jdbc.object.SqlUpdate : This class has too many constructors (4). Consider using static factory methods or the Builder pattern
org.springframework.jdbc.object.SqlFunction : This class has too many constructors (4). Consider using static factory methods or the Builder pattern
org.springframework.jdbc.datasource.DriverManagerDataSource : This class has too many constructors (4). Consider using static factory methods or the Builder pattern
org.springframework.jdbc.datasource.SingleConnectionDataSource : This class has too many constructors (4). Consider using static factory methods or the Builder pattern
org.springframework.jdbc.datasource.SimpleDriverDataSource : This class has too many constructors (4). Consider using static factory methods or the Builder pattern
org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator : This class has too many constructors (4). Consider using static factory methods or the Builder pattern
org.springframework.jdbc.core.SqlOutParameter : This class has too many constructors (7). Consider using static factory methods or the Builder pattern
org.springframework.jdbc.core.SqlParameter : This class has too many constructors (7). Consider using static factory methods or the Builder pattern
org.springframework.jdbc.core.support.SqlLobValue : This class has too many constructors (8). Consider using static factory methods or the Builder pattern
org.springframework.jdbc.core.ResultSetSupportingSqlParameter : This class has too many constructors (6). Consider using static factory methods or the Builder pattern
org.springframework.jdbc.core.SqlParameterValue : This class has too many constructors (4). Consider using static factory methods or the Builder pattern
org.springframework.jdbc.core.SqlInOutParameter : This class has too many constructors (7). Consider using static factory methods or the Builder pattern
japa.parser.ast.stmt.CatchClause.CatchClause(int, int, int, int, int, List<AnnotationExpr>, List<Type>, VariableDeclaratorId, BlockStmt) : This constructor has too many parameters (9). Consider using the Builder pattern
japa.parser.ast.stmt.LabeledStmt.LabeledStmt(int, int, int, int, String, Statement) : This constructor has too many parameters (6). Consider using the Builder pattern
japa.parser.ast.stmt.DoStmt.DoStmt(int, int, int, int, Statement, Expression) : This constructor has too many parameters (6). Consider using the Builder pattern
japa.parser.ast.stmt.AssertStmt.AssertStmt(int, int, int, int, Expression, Expression) : This constructor has too many parameters (6). Consider using the Builder pattern
japa.parser.ast.stmt.ExplicitConstructorInvocationStmt.ExplicitConstructorInvocationStmt(int, int, int, int, List<Type>, boolean, Expression, List<Expression>) : This constructor has too many parameters (8). Consider using the Builder pattern
japa.parser.ast.stmt.SwitchStmt.SwitchStmt(int, int, int, int, Expression, List<SwitchEntryStmt>) : This constructor has too many parameters (6). Consider using the Builder pattern
japa.parser.ast.stmt.IfStmt.IfStmt(int, int, int, int, Expression, Statement, Statement) : This constructor has too many parameters (7). Consider using the Builder pattern
japa.parser.ast.stmt.SwitchEntryStmt.SwitchEntryStmt(int, int, int, int, Expression, List<Statement>) : This constructor has too many parameters (6). Consider using the Builder pattern
japa.parser.ast.stmt.SynchronizedStmt.SynchronizedStmt(int, int, int, int, Expression, BlockStmt) : This constructor has too many parameters (6). Consider using the Builder pattern
japa.parser.ast.stmt.ForeachStmt.ForeachStmt(int, int, int, int, VariableDeclarationExpr, Expression, Statement) : This constructor has too many parameters (7). Consider using the Builder pattern
japa.parser.ast.stmt.ForStmt.ForStmt(int, int, int, int, List<Expression>, Expression, List<Expression>, Statement) : This constructor has too many parameters (8). Consider using the Builder pattern
japa.parser.ast.stmt.WhileStmt.WhileStmt(int, int, int, int, Expression, Statement) : This constructor has too many parameters (6). Consider using the Builder pattern
japa.parser.ast.stmt.TryStmt.TryStmt(int, int, int, int, List<VariableDeclarationExpr>, BlockStmt, List<CatchClause>, BlockStmt) : This constructor has too many parameters (8). Consider using the Builder pattern
japa.parser.ast.body.AnnotationMemberDeclaration.AnnotationMemberDeclaration(int, int, int, int, int, List<AnnotationExpr>, Type, String, Expression) : This constructor has too many parameters (9). Consider using the Builder pattern
japa.parser.ast.body.EnumDeclaration.EnumDeclaration(int, List<AnnotationExpr>, String, List<ClassOrInterfaceType>, List<EnumConstantDeclaration>, List<BodyDeclaration>) : This constructor has too many parameters (6). Consider using the Builder pattern
japa.parser.ast.body.EnumDeclaration.EnumDeclaration(int, int, int, int, int, List<AnnotationExpr>, String, List<ClassOrInterfaceType>, List<EnumConstantDeclaration>, List<BodyDeclaration>) : This constructor has too many parameters (10). Consider using the Builder pattern
japa.parser.ast.body.Parameter.Parameter(int, int, int, int, int, List<AnnotationExpr>, Type, boolean, VariableDeclaratorId) : This constructor has too many parameters (9). Consider using the Builder pattern
japa.parser.ast.body.EnumConstantDeclaration.EnumConstantDeclaration(int, int, int, int, List<AnnotationExpr>, String, List<Expression>, List<BodyDeclaration>) : This constructor has too many parameters (8). Consider using the Builder pattern
japa.parser.ast.body.VariableDeclarator.VariableDeclarator(int, int, int, int, VariableDeclaratorId, Expression) : This constructor has too many parameters (6). Consider using the Builder pattern
japa.parser.ast.body.TypeDeclaration.TypeDeclaration(int, int, int, int, List<AnnotationExpr>, int, String, List<BodyDeclaration>) : This constructor has too many parameters (8). Consider using the Builder pattern
japa.parser.ast.body.VariableDeclaratorId.VariableDeclaratorId(int, int, int, int, String, int) : This constructor has too many parameters (6). Consider using the Builder pattern
japa.parser.ast.body.BaseParameter.BaseParameter(int, int, int, int, int, List<AnnotationExpr>, VariableDeclaratorId) : This constructor has too many parameters (7). Consider using the Builder pattern
japa.parser.ast.body.AnnotationDeclaration.AnnotationDeclaration(int, int, int, int, int, List<AnnotationExpr>, String, List<BodyDeclaration>) : This constructor has too many parameters (8). Consider using the Builder pattern
japa.parser.ast.body.MethodDeclaration.MethodDeclaration(int, List<AnnotationExpr>, List<TypeParameter>, Type, String, List<Parameter>, int, List<NameExpr>, BlockStmt) : This constructor has too many parameters (9). Consider using the Builder pattern
japa.parser.ast.body.MethodDeclaration.MethodDeclaration(int, int, int, int, int, List<AnnotationExpr>, List<TypeParameter>, Type, String, List<Parameter>, int, List<NameExpr>, BlockStmt) : This constructor has too many parameters (13). Consider using the Builder pattern
japa.parser.ast.body.InitializerDeclaration.InitializerDeclaration(int, int, int, int, boolean, BlockStmt) : This constructor has too many parameters (6). Consider using the Builder pattern
japa.parser.ast.body.FieldDeclaration.FieldDeclaration(int, int, int, int, int, List<AnnotationExpr>, Type, List<VariableDeclarator>) : This constructor has too many parameters (8). Consider using the Builder pattern
japa.parser.ast.body.ConstructorDeclaration.ConstructorDeclaration(int, List<AnnotationExpr>, List<TypeParameter>, String, List<Parameter>, List<NameExpr>, BlockStmt) : This constructor has too many parameters (7). Consider using the Builder pattern
japa.parser.ast.body.ConstructorDeclaration.ConstructorDeclaration(int, int, int, int, int, List<AnnotationExpr>, List<TypeParameter>, String, List<Parameter>, List<NameExpr>, BlockStmt) : This constructor has too many parameters (11). Consider using the Builder pattern
japa.parser.ast.body.MultiTypeParameter.MultiTypeParameter(int, int, int, int, int, List<AnnotationExpr>, List<Type>, VariableDeclaratorId) : This constructor has too many parameters (8). Consider using the Builder pattern
japa.parser.ast.body.ClassOrInterfaceDeclaration.ClassOrInterfaceDeclaration(int, List<AnnotationExpr>, boolean, String, List<TypeParameter>, List<ClassOrInterfaceType>, List<ClassOrInterfaceType>, List<BodyDeclaration>) : This constructor has too many parameters (8). Consider using the Builder pattern
japa.parser.ast.body.ClassOrInterfaceDeclaration.ClassOrInterfaceDeclaration(int, int, int, int, int, List<AnnotationExpr>, boolean, String, List<TypeParameter>, List<ClassOrInterfaceType>, List<ClassOrInterfaceType>, List<BodyDeclaration>) : This constructor has too many parameters (12). Consider using the Builder pattern
japa.parser.ast.type.ClassOrInterfaceType.ClassOrInterfaceType(int, int, int, int, ClassOrInterfaceType, String, List<Type>) : This constructor has too many parameters (7). Consider using the Builder pattern
japa.parser.ast.type.WildcardType.WildcardType(int, int, int, int, ReferenceType, ReferenceType) : This constructor has too many parameters (6). Consider using the Builder pattern
japa.parser.ast.type.ReferenceType.ReferenceType(int, int, int, int, Type, int) : This constructor has too many parameters (6). Consider using the Builder pattern
japa.parser.ast.PackageDeclaration.PackageDeclaration(int, int, int, int, List<AnnotationExpr>, NameExpr) : This constructor has too many parameters (6). Consider using the Builder pattern
japa.parser.ast.expr.FieldAccessExpr.FieldAccessExpr(int, int, int, int, Expression, List<Type>, String) : This constructor has too many parameters (7). Consider using the Builder pattern
japa.parser.ast.expr.ArrayCreationExpr.ArrayCreationExpr(int, int, int, int, Type, int, ArrayInitializerExpr) : This constructor has too many parameters (7). Consider using the Builder pattern
japa.parser.ast.expr.ArrayCreationExpr.ArrayCreationExpr(int, int, int, int, Type, List<Expression>, int) : This constructor has too many parameters (7). Consider using the Builder pattern
japa.parser.ast.expr.VariableDeclarationExpr.VariableDeclarationExpr(int, int, int, int, int, List<AnnotationExpr>, Type, List<VariableDeclarator>) : This constructor has too many parameters (8). Consider using the Builder pattern
japa.parser.ast.expr.SingleMemberAnnotationExpr.SingleMemberAnnotationExpr(int, int, int, int, NameExpr, Expression) : This constructor has too many parameters (6). Consider using the Builder pattern
japa.parser.ast.expr.InstanceOfExpr.InstanceOfExpr(int, int, int, int, Expression, Type) : This constructor has too many parameters (6). Consider using the Builder pattern
japa.parser.ast.expr.ObjectCreationExpr.ObjectCreationExpr(int, int, int, int, Expression, ClassOrInterfaceType, List<Type>, List<Expression>, List<BodyDeclaration>) : This constructor has too many parameters (9). Consider using the Builder pattern
japa.parser.ast.expr.AssignExpr.AssignExpr(int, int, int, int, Expression, Expression, Operator) : This constructor has too many parameters (7). Consider using the Builder pattern
japa.parser.ast.expr.NormalAnnotationExpr.NormalAnnotationExpr(int, int, int, int, NameExpr, List<MemberValuePair>) : This constructor has too many parameters (6). Consider using the Builder pattern
japa.parser.ast.expr.QualifiedNameExpr.QualifiedNameExpr(int, int, int, int, NameExpr, String) : This constructor has too many parameters (6). Consider using the Builder pattern
japa.parser.ast.expr.MemberValuePair.MemberValuePair(int, int, int, int, String, Expression) : This constructor has too many parameters (6). Consider using the Builder pattern
japa.parser.ast.expr.ArrayAccessExpr.ArrayAccessExpr(int, int, int, int, Expression, Expression) : This constructor has too many parameters (6). Consider using the Builder pattern
japa.parser.ast.expr.MethodCallExpr.MethodCallExpr(int, int, int, int, Expression, List<Type>, String, List<Expression>) : This constructor has too many parameters (8). Consider using the Builder pattern
japa.parser.ast.expr.ConditionalExpr.ConditionalExpr(int, int, int, int, Expression, Expression, Expression) : This constructor has too many parameters (7). Consider using the Builder pattern
japa.parser.ast.expr.CastExpr.CastExpr(int, int, int, int, Type, Expression) : This constructor has too many parameters (6). Consider using the Builder pattern
japa.parser.ast.expr.BinaryExpr.BinaryExpr(int, int, int, int, Expression, Expression, Operator) : This constructor has too many parameters (7). Consider using the Builder pattern
japa.parser.ast.expr.UnaryExpr.UnaryExpr(int, int, int, int, Expression, Operator) : This constructor has too many parameters (6). Consider using the Builder pattern
japa.parser.ast.CompilationUnit.CompilationUnit(int, int, int, int, PackageDeclaration, List<ImportDeclaration>, List<TypeDeclaration>) : This constructor has too many parameters (7). Consider using the Builder pattern
japa.parser.ast.TypeParameter.TypeParameter(int, int, int, int, String, List<ClassOrInterfaceType>) : This constructor has too many parameters (6). Consider using the Builder pattern
japa.parser.ast.ImportDeclaration.ImportDeclaration(int, int, int, int, NameExpr, boolean, boolean) : This constructor has too many parameters (7). Consider using the Builder pattern

Обратите внимание, что EffectiveJava не выполняет подсветку синтаксиса: это виджет, который я использую для показа кода (что за грязь, а?).

Как это реализовано

Различные запросы реализованы как  Операция :

(defrecord Operation [query params headers])

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

Таким образом, операция может использоваться с  printOperation :

(defn printOperation [operation cus threshold]
  (let [headers (.headers operation),
        results ((.query operation) {:cus cus :threshold threshold})]
    (printTable headers results)))

printTable  содержит всю логику для создания (вроде) красивой карты. Обратите внимание, что в результате запроса могут содержаться объекты другого типа: классы, методы, конструкторы, поля и т. Д. Мне нужен способ преобразовать их все в строки. Для этого я использую мульти-метод Clojure:

(defmulti toString class)

(defmethod toString :default [x]
  (str x))

(defmethod toString String [x]
  x)

(defmethod toString clojure.lang.Keyword [x]
  (name x))

(defmethod toString ClassOrInterfaceDeclaration [x]
  (getQName x))

(defmethod toString ConstructorDeclaration [x]
  (getQName x))

Например, я печатаю полное имя ( getQName ) для разных элементов. То, как он рассчитывается, использует протокол Clojure. Это часть реализации:

(defprotocol withPackageName
  (packageName [this]))

(extend-protocol withPackageName
  CompilationUnit
  (packageName [this]
    (let
      [p (.getPackage this)]
      (if (nil? p)
        ""
        (.toString (.getName p))))))

(extend-protocol withPackageName
  Node
  (packageName [this]
    (let [pn (.getParentNode this)]
      (packageName pn))))

(extend-protocol withPackageName
  SingleFieldDeclaration
  (packageName [this]
    (packageName (.variable this))))

(defprotocol Named
  (getName [this])
  (getQName [this]))

(extend-protocol Named
  TypeDeclaration
  (getName [this]
    (.getName this))
  (getQName [this]
    (let
      [pn (packageName this),
       cn (getName this)]
      (if (.isEmpty pn)
        cn
        (str pn "." cn)))))

(extend-protocol Named
  EnumConstantDeclaration
  (getName [this]
    (.getName this))
  (getQName [this]
    (let [pn (.getParentNode this)]
      (str (getQName pn) "." (getName this)))))

(extend-protocol Named
  MethodDeclaration
  (getName [this]
    (.getName this))
  (getQName [this]
    (let [pn (.getParentNode this)]
      (str (getQName pn) "." (getName this)))))

Для интерактивного режима мы используем insta-parser для разбора команд:

(def command-parser
  (insta/parser
    (str
      "<COMMAND> = HELP | EXIT | LOAD | LIST | MC            \n"
      "HELP = 'help' | 'h'                                   \n"
      "EXIT = 'exit' | 'quit' | 'q'                          \n"
      "LOAD = 'load' <WS> STR                                \n"
      "LIST = 'list'                                         \n"
      "MC  = ('mc'|'many-constructors') <WS> 'th' <WS> NUM   \n"
      "WS  = #'[\t ]+'                                       \n"
      "NUM = #'[0-9]+'                                       \n"
      "STR = #'\"[^\"]*\"'                                   \n")))

Тогда у нас есть просто цикл, передающий в состоянии список загруженных классов.

Для разбора параметров, полученных из командной строки, мы просто используем параметры разбора:

(def cliOpts [
               ["-h" "--help" "Show help" :flag true :default false]
               ["-i" "--interactive" "launch interactive mode" :flag true :default false]
               ["-l" "--linter" "launch linter mode" :flag true :default false]
               ["-d" "--dir DIRNAME" "REQUIRED: Directory containing the code to check"]
               ["-q" "--query QUERYNAME" "REQUIRED: Query to perform: mc=many constructors, mcp=many constructor parameters, st=singleton type"]
               ["-t" "--threshold VALUE" "Threshold to be used in the query" :default 0
                :parse-fn #(Integer/parseInt %)
                :validate [#(>= % 0) "Must be a number equal or greater to 0"]]
               ])

(defn name2operation [name]
  (cond
    (= "mc" name) classesWithManyConstructorsOp
    (= "mcp" name) constructorsWithManyParametersOp
    (= "st" name) classesAndSingletonTypeOp
    :else nil))


(defn -main
  "What I do"
  [& args]
  (let [optsMap (parse-opts args cliOpts)
        opts (:options optsMap)
        banner (:summary optsMap)]
     ...
     ...
     ... the rest of the good stuff

Затем есть некоторый код для взаимодействия инструмента с  Javaparser,  но вам повезло: я не буду утомлять вас этим. Если вам интересно, не стесняйтесь писать мне: мне нравится помогать, и мне нравится Javaparser, поэтому я очень рад ответить на все ваши вопросы по этому поводу.

Выводы

Идея создания инструментов для изучения кодовых баз меня давно заинтересовала. Во время моей кандидатской диссертации я создал несколько инструментов в этой общей области, например CodeModels, который включает в себя несколько анализаторов для нескольких языков и позволяет выполнять межязыковой анализ.

Что вы думаете? Стоит ли уделять этому инструменту больше времени? Хотите использовать что-то подобное? Какова ваша стратегия для статического анализа?