GithubHelp home page GithubHelp logo

ftomassetti / effectivejava Goto Github PK

View Code? Open in Web Editor NEW
504.0 38.0 45.0 14.25 MB

Run queries on your Java code to check if it meets the criteria suggested by the book Effective Java. And some others.

License: Apache License 2.0

Java 95.28% HTML 0.01% Clojure 4.71%

effectivejava's Introduction

effectivejava

Build Status

Effective java is a tool to examine your Java codebase. You can use it in three different ways:

  • as a Java linter. Just tells it which directory you want to examine, it will spit out a set of warnings and suggestions to improve your code
  • run queries from the command line. It could tell you which type of Singleton you are using in your codebase or how many constructors have 10 or more parameters. This modality can be easily integrated with other tools
  • run queries interactively. It permits to poke your codebase, parsing it once and running different queries to find out interesting facts about it.

The project is named effectivejava because many queries/checks derive from reading the book Effective Java. Others will be implemented as well (feel free to suggest your favorite ones!).

While reading that book I thought that yes, many principles are well known, but they are rarely applied to a large codebase. I thought that applying them in practice is much harder than it seems, and a tool like this one could help in improving constantly a codebase.

Which is the easiest way to install it?

Download the standalone jar from the releases page. No deps needed, everything is packed inside the jar. Feel free to rename it (effectivejava-0.1.0-SNAPSHOT-standalone.jar is a mouthful...)

Linting mode: how to use it

Just run:

# this generate a jar file
lein jar
# note that 0.1.3 is the current version it could change in the future
java -jar effectivejava-0.1.3-SNAPSHOT-standalone.jar -l -d "<myJavaProjectDir>"

You can expect a set of lines like this one:

org.springframework.jdbc.core.SqlInOutParameter : This class has too many constructors (7). Consider using static factory methods or the Builder pattern

If you run this command from the root of your codebase you can avoid the -d option.

CLI mode: how to use it

Now, suppose you want to know which classes has 5 or more constructor; you can run this command:

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

You can expect a similar output:

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

Interactive mode: how to use it (Work in progress!)

You can launch interactive mode with the -i option.

java -jar effectivejava-0.1.3-SNAPSHOT-standalone.jar -i

A typical interaction could be this one:

> load "."
Loading .
Java files loaded: 440
> mc th 5
Command not implemented:  :MC

As you can read from the last line, while the main logic for the interactive mode is there we still miss a few bits :) It will be corrected soon.

What queries can you run

I am just getting started so I implemented only a few queries for now:

  • mc=many constructors: find the classes which contain the same number of constructors as the given threshold, or more
  • mcp=many constructor parameters: find the constructors which contain the same number of parameters as the given threshold, or more
  • st=singleton type: find if a type implements the singleton pattern and distinguish between the three types (public field, static factory, singleton enum)
  • u=utils classes: find classes having only static methods and verify they have exactly one private constructor taking no parameters

Effective Java (the book) items implemented

Item Status
Item 1 Done
item 2 TODO
item 3 Done
item 4 Done
item 5 TODO
item 6 TODO
item 7 Done
item 8 Planned for v0.2
item 9 Planned for v0.2
item 10 Done
item 11 TODO
...item 78 TODO

Dev info

The project is written in Clojure using a java library called JavaParser.

You will need also Leiningen, the build tool for Clojure. It should download also Clojure for you.

Dev guidelines

To monitor code quality we use kibit and eastwood.

When running eastwood exclude the check for unlimited use of namespaces:

lein eastwood "{:exclude-linters [:unlimited-use]}"

To verify the code is correctly formatted cljfmt is used. You can run it like this:

# to verify possible style issues
lein cljfmt check
# to automatically fix them
lein cljfmt fix

We use lein-ancient to verify our dependencies are up-to-date.

What is the link with the book?

I am reading this book and many advices seem sort of obvious in theory but I guess there are some violations lurking in the large codebase I am working with. I was curious to assess how many violations there were and I needed a way to find them out automatically. And I wanted to learn Clojure. And I had a free sunday. So...

What else

Hope you enjoy this small project of mine. Feel free to open issues and ask questions!

Contributors

David Ortiz is a regular contributor: he started fixing bugs, setting up Travis and it is contributing many other improvements.

effectivejava's People

Contributors

davidor avatar ftomassetti avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

effectivejava's Issues

Build a symbol solver

Javaparser does not solve symbols, it just builds ASTs. We need to do that and to do that we need also to examine Jar files and binary components. I should have some initial Java code somewhere to be used as an initial inspiration. Fun times are ahead...

Command not implemented in interactive mode closes program

In the interactive mode, ff the user tries to execute a command that is not implemented, he gets an error such as: Command not implemented: , and the program closes.

I think that it would be more natural to show the error and continue the execution.

Testing withModifiers protocol

Does it work for SingleFieldDeclaration?
Does it work for MethodDeclaration?
Does it work for TypeDeclaration?
Does it work for FieldDeclaration?

Issue 17

Constructors of non final classes should not invoke overridable methods

Item 11: Cloneable

Clone should return the same class of the defining class and do not declare to throw CloneNotSUpportedException.

They should also not call not-final methods.

Unable to run standalone 0.1.2-snapshot on windows

So, downloaded the 01.2-SNAPSHOT.jar file. Trying to run it on Windows 7 with java 8:

$ java -version
java version "1.8.0_31"
Java(TM) SE Runtime Environment (build 1.8.0_31-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.31-b07, mixed mode)

Both from bash and from the cmd prompt, I get a null pointer exception:

C:\Users\dan.moore\project\effectivejava>java -jar effectivejava-0.1.2-SNAPSHOT-standalone.jar
Exception in thread "main" java.lang.ExceptionInInitializerError
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:344)
        at clojure.lang.RT.loadClassForName(RT.java:2093)
        at clojure.lang.RT.load(RT.java:430)
        at clojure.lang.RT.load(RT.java:411)
        at clojure.core$load$fn__5066.invoke(core.clj:5641)
        at clojure.core$load.doInvoke(core.clj:5640)
        at clojure.lang.RestFn.invoke(RestFn.java:408)
        at app.core__init.load(Unknown Source)
        at app.core__init.<clinit>(Unknown Source)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:344)
        at clojure.lang.RT.loadClassForName(RT.java:2093)
        at clojure.lang.RT.load(RT.java:430)
        at clojure.lang.RT.load(RT.java:411)
        at clojure.core$load$fn__5066.invoke(core.clj:5641)
        at clojure.core$load.doInvoke(core.clj:5640)
        at clojure.lang.RestFn.invoke(RestFn.java:408)
        at clojure.lang.Var.invoke(Var.java:379)
        at app.core.<clinit>(Unknown Source)
Caused by: java.lang.NullPointerException
        at app.javaparser__init.load(Unknown Source)
        at app.javaparser__init.<clinit>(Unknown Source)
        ... 20 more

I get a similar error when running it in lint mode.

Organize namespaces

Probably app is a very bad name for the namespace. We should rename all of them and then we could use import-vars from https://github.com/ztellman/potemkin to import and re-export the symbols defined in the namespace. For example we could have one namespace name app.model.facade which expose all the content of all the namespaces with name app.model.*

SolveSuperclass

Given this example:

class B extends A {
}

class C extends B {
}

We need to find the superclasses of C (A and B).

We have most of the logic in place for that, but I could write the last bit for this particular case.

I would write a function named "solveSuperclass" in funcs.clj which would get a ClassDeclaration instance and basically call

(solveClass  classDeclaration nil (.getSuperclass classDeclaration)

solveClass is a method from a protocol.

Once we have this function we can create getAllSuperclasses calling recursively solveSuperclass.

Java linter modality in combination with svn diff / git diff

Ideally I would know which errors/warnings refer to the lines/classes I am changing, so I would like to somehow pipe it with svn diff or git diff.

While it could be easy to assign each warning to a class, it could be trickier to assign specific lines.

I should think about that.

mcp query fails when no threshold is specified

When I execute lein run -- --query mcp the program raises a NullPointerException.
It does not happen if I specify a threshold: lein run -- --query mcp -t 1, but raises the same kind of exception if the threshold is <= 0.

Item 7: finalizers

We could recognize classes which has finalizers. This one should be easy.

Increase code coverage

Running lein cloverage I get this:

Produced output in /home/federico/repos/effectivejava/target/coverage .
HTML: file:///home/federico/repos/effectivejava/target/coverage/index.html

|                         :name | :forms_percent | :lines_percent |
|-------------------------------+----------------+----------------|
|                       app.cli |        63.27 % |        33.33 % |
|                      app.core |        12.45 % |        27.12 % |
|               app.interactive |        10.39 % |        21.79 % |
|          app.itemsOnLifecycle |        72.66 % |        81.37 % |
|                app.jarloading |        97.20 % |        96.88 % |
|     app.javaparser.navigation |        63.34 % |        94.12 % |
|        app.javaparser.parsing |        98.68 % |       100.00 % |
|                    app.linter |        17.44 % |        45.83 % |
|          app.model.javaparser |        61.66 % |        78.17 % |
|           app.model.javassist |        90.91 % |        66.67 % |
|                app.operations |        60.80 % |        82.14 % |
|       app.symbol_solver.scope |        97.85 % |        98.89 % |
| app.symbol_solver.type_solver |        90.91 % |       100.00 % |
|                     app.utils |        92.96 % |        94.44 % |
Files with 100% coverage: 2

Forms covered: 59.73 %
Lines covered: 75.79 %

We should first of all organize better the tests (especially w.r.t the new modules for symbol resolution) and then focus on improving code coverage where possible. I would try to track progress in this issue.

Implement Item 4

We want to recognize Utils classes. We could use the name and/or look for classes which has only static methods and fields (and at least one method)

Confusing behavior enter key in interactive mode

When in the interactive mode, if the user presses the enter key, he will get an error like this:

ERROR: Parse error at line 1, column 1:
nil
^
Expected one of:
singletons (followed by end-of-string)
st (followed by end-of-string)
finalizers (followed by end-of-string)
f (followed by end-of-string)
list (followed by end-of-string)
q (followed by end-of-string)
quit (followed by end-of-string)
exit (followed by end-of-string)
h (followed by end-of-string)
help (followed by end-of-string)
many-costructor-params
mcp
many-constructors
mc
load

Wouldn't it be more intuitive to get something like this when pressing enter multiple times?

If you think so, I can fix this.

Item 8: float and double fields

In the equals method float and double fields should be compared using Float.compare and Double.compare because of the existence of NaN and -0.0f (see Float documentation).

We could write a query that check that.

Threshold for mc query is ignored in interactive mode

The threshold seems to be set to 5 ignoring the user input.
For example, using the examples classes included in the project:

mc th 7

class | numberOfConstructors

japa.parser.ast.body.BaseParameter | 5
japa.parser.ast.body.FieldDeclaration | 5
japa.parser.ast.body.MethodDeclaration | 5
japa.parser.ast.expr.ArrayCreationExpr | 5
org.springframework.jdbc.core.ResultSetSupportingSqlParameter | 6
org.springframework.jdbc.core.SqlInOutParameter | 7
org.springframework.jdbc.core.SqlOutParameter | 7
org.springframework.jdbc.core.SqlParameter | 7
org.springframework.jdbc.core.support.SqlLobValue | 8

Name collision warning

I get this warning while executing the program:

WARNING: Named already refers to: interface clojure.lang.Named in namespace: app.model.javaparser, being replaced by: #'app.model.protocols/Named

Maven plugin

Please create a Maven wrapper plugin so that this tool can be used easily in automated builds

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.