My primary motivation is making the `mvn dependency:tree -DoutputType=dot`
output usable. That's why I've been looking at that code - I've
used 'dependency' for years.

I can definitely look at repeating the effort but unless the existing
implementation is going away soon I would prefer to finish my current work
and then circle back to this. (This is actually about 3 levels deep from my
primary focus - it's been a series of "oh, if I have *this* then I can do
*that* more quickly. I would like to get back to those other tasks asap.)

I'm having trouble creating a PR so I can point you to what I have as a
start. (I suspect it's because I added an 'examples' directory so I can
include links instead of attachments.)

The fork is at https://github.com:/coyotesong/maven-dependency-plugin.

This directory contains the results of running 'mvn dependency:tree' on
this project.
https://github.com/coyotesong/maven-dependency-plugin/tree/master/examples.
The generated image files are located in
https://github.com/coyotesong/maven-dependency-plugin/tree/master/examples/dot/3.8.2-SNAPSHOT
.

The proposed changes are pretty much all in this package:
https://github.com/coyotesong/maven-dependency-plugin/tree/add-velocity-utils/src/main/java/org/apache/maven/plugins/dependency/utils/templates

There's also the test class, some test templates, etc.

Bear



On Sun, Mar 30, 2025 at 1:57 PM Guillaume Nodet <gno...@apache.org> wrote:

> Which project are you targeting exactly ?
> If you're targeting Maven Core, do you think you could target the new API
> rather than the resolver one ?
>
>
> https://github.com/apache/maven/blob/master/api/maven-api-core/src/main/java/org/apache/maven/api/Node.java
> Or are you targeting the resolver ?
>
> Le dim. 30 mars 2025 à 19:04, Bear Giles <bgi...@coyotesong.com> a écrit :
>
> > I've taken the time to look around the other 'tree' classes, or more
> > precisely the '*DependencyNodeVisitor" classes, and noticed that most if
> > not all of them follow the anti-pattern of doing more than one thing in
> > each method. More precisely they mix logic ('what to include') and
> > presentation ('what to send to the writer). This makes code difficult to
> > understand, to test, to extend by a third party, etc.
> >
> > This is why template engines were developed! :-)
> >
> > Fortunately there's already a dependency on 'velocity', although it
> appears
> > to only be used during the extensive testing. However it's still there in
> > the 'compile' scope so using it won't require adding new dependencies.
> >
> > That's a very clean solution to the problem identified above. For most
> > formats the velocity template won't need anything beyond what's already
> > included in the WrappedNode (DependencyNode). If a format can benefit
> from
> > adding a tiny bit of additional information it can be added to the
> > WrappedNode since that class is exclusive to the 'tree' package. In more
> > complex cases the individual class can maintain it's own data structures
> > and add them to the Context provided to velocity.
> >
> > This should work with all of the existing classes although I'm only
> > planning to modify one at this time. It should make the classes much
> easier
> > to maintain since they can focus on the logic of how to convert the data
> > provided by the DependencyNode to the data usable by the Velocity
> engine. I
> > know other template engines, e.g., jinjava, have a way to provide
> > user-defined functions but I don't think velocity does - that's
> unfortunate
> > since it will require a modest amount of pre-computation. Fortunately
> that
> > can be handled separately, immediately before calling the Velocity
> 'merge'
> > method.
> >
> > This will only require a few classes - basically just a wrapper to set up
> > the velocity engine. It will also require a standard place to put the
> > templates and I recommend 'templates/{format}/{name} and
> > 'templates/{format}/macros/{name}. The 'format' would be 'dot', 'json',
> > etc., and the 'macros' allows velocity macros to be kept separately from
> > the templates since they're much more likely to be reused. (Hmm...
> perhaps
> > also 'templates/macros/' for macros that are format-agnostic.
> >
> > -----------------------------
> >
> > The second PR will probably be a bit more controversial since there's
> > multiple ways to implement it and comments in some of the 'upstream'
> > methods suggest there's already WIP that may affect how the data is
> > provided to the *DependencyNodeVisitor classes.
> >
> > At the moment these classes only include two methods - visit(node) and
> > endVisit(node). It is the responsibility of the endVisit(node) method to
> > determine that all data has been received. I think that's an obvious
> > omission but the DependencyNodeVisitor interface is defined in a
> different
> > project. It would be trivial to add to the existing implementations, and
> > java8 supports optional methods so there would be no impact on others if
> > this method is added to the interface with an empty optional method.
> >
> > That's not a big problem if your visitor is able to run in a 'streaming'
> > mode, but some formats can benefit from using a 'document' mode instead.
> > In most cases this doesn't matter since the results are not intended for
> > human consumption - they need to be a standard format accepted by other
> > applications and these formats are usually designed to work in a
> streaming
> > fashion.
> >
> > However the dot format, and potentially other, are intended for human
> > consumption. The file itself isn't human-friendly but there are widely
> > available tools to convert it into human-friendly images and even
> > interactive formats like svg. In these cases it makes much more sense  to
> > use a document mode when creating the final document since it is often
> > appropriate to provide the same information, or at least related
> > information, in multiple places in the document.
> >
> > So there's clearly a benefit to a new method that is called immediately
> > after the final endVisit(), one that provides the complete dependency
> tree.
> > The implementations should be able to ignore visit() and endVisit(). (And
> > in fact they can be added to the abstract class if not the
> > DependencyNodeVisitor interface)
> >
> > The question is how to implement this, esp. when looking at other
> potential
> > enhancements to the class.
> >
> > For instance it's possible to have DependencyNodeVisitor implement
> > Consumer<DependencyNode> and the 'accept()' method calls visit(). It may
> > not be practical since many implementations will need the nesting that
> > endVisit() provides.
> >
> > Another possibility is to have DependencyNodeVisitor implement
> > Function<DependencyNode, Boolean> that's called after the final
> endVisit()
> > call. Again the idea is to simplify integration into streams but there's
> no
> > reason you couldn't just use the (object::function) notation.
> >
> > I think this ultimately comes down to what's best with future
> improvements
> > to the upstream code that creates the data sent to these classes. There
> are
> > notes that indicate the desire to shift to a streaming pattern (while
> > maintaining legacy interfaces, of course) and it would make sense to use
> a
> > consistent approach here - even if it means reformatting the data
> > immediately before calling the new method.
> >
> > For now I'll just add a new method to the abstract class that all of the
> > implementations extend but I consider it a stopgap measure since this
> > functionality would be useful to third party developers, etc.
> >
> > (Sidenote - it could even quietly replace some of the existing logic
> since
> > the final method could easily perform a recursive descent of rootNode and
> > call visit() and endVisit() itself. I think this could be put into the
> > DependencyNodeVisitor interface as a default method so there would be no
> > risk to removing the existing code. The primary drawback would be that
> the
> > current approach allows the implementation to bail out early if it hits a
> > problem while this approach would require the entire rootNode structure
> to
> > be generated first.)
> >
> > Bear
> >
>
>
> --
> ------------------------
> Guillaume Nodet
>

Reply via email to