Initial commit
authorSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Fri, 17 Apr 2026 14:48:22 +0000 (17:48 +0300)
committerSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Fri, 17 Apr 2026 14:48:22 +0000 (17:48 +0300)
41 files changed:
.gitignore [new file with mode: 0755]
AGENTS.md [new file with mode: 0644]
COPYING [new file with mode: 0644]
TODO.org [new file with mode: 0644]
Tools/Open with IntelliJ IDEA [new file with mode: 0755]
Tools/Update web site [new file with mode: 0755]
commandline launcher/install [new file with mode: 0755]
commandline launcher/javainspect [new file with mode: 0755]
doc/JavaInspect full project.png [new file with mode: 0644]
doc/JavaInspect.dot [new file with mode: 0644]
doc/JavaInspect.png [new file with mode: 0644]
doc/example-thumbnail.png [new file with mode: 0644]
doc/example.png [new file with mode: 0644]
doc/index.org [new file with mode: 0644]
doc/legend.png [new file with mode: 0644]
doc/usage cli.org [new file with mode: 0644]
pom.xml [new file with mode: 0644]
src/main/java/eu/svjatoslav/inspector/java/RenderJavaInspect.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/inspector/java/commandline/CommandlineConfiguration.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/inspector/java/commandline/Main.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/inspector/java/commandline/TargetImageTypeParameter.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/inspector/java/commandline/package-info.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/inspector/java/structure/ClassDescriptor.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/inspector/java/structure/ClassGraph.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/inspector/java/structure/FieldDescriptor.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/inspector/java/structure/GraphElement.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/inspector/java/structure/MethodDescriptor.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/inspector/java/structure/TargetImageType.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/inspector/java/structure/Utils.java [new file with mode: 0755]
src/main/java/eu/svjatoslav/inspector/java/structure/package-info.java [new file with mode: 0755]
usage examples/demo project/.gitignore [new file with mode: 0644]
usage examples/demo project/pom.xml [new file with mode: 0644]
usage examples/demo project/src/main/java/eu/svjatoslav/inspector/java/structure/example/RenderUsingReflection.java [new file with mode: 0755]
usage examples/demo project/src/main/java/eu/svjatoslav/inspector/java/structure/example/torender/ObjectReturnedByMethod.java [new file with mode: 0755]
usage examples/demo project/src/main/java/eu/svjatoslav/inspector/java/structure/example/torender/ObjectVisibleAsClassField.java [new file with mode: 0755]
usage examples/demo project/src/main/java/eu/svjatoslav/inspector/java/structure/example/torender/SampleClass.java [new file with mode: 0755]
usage examples/demo project/src/main/java/eu/svjatoslav/inspector/java/structure/example/torender/SampleClass2.java [new file with mode: 0644]
usage examples/demo project/src/main/java/eu/svjatoslav/inspector/java/structure/example/torender/SampleEnum.java [new file with mode: 0755]
usage examples/demo project/src/main/java/eu/svjatoslav/inspector/java/structure/example/torender/SampleInterface.java [new file with mode: 0755]
usage examples/demo project/src/main/java/eu/svjatoslav/inspector/java/structure/example/torender/SampleSuperClass.java [new file with mode: 0755]
usage examples/demo project/tools/open with IntelliJ IDEA [new file with mode: 0755]

diff --git a/.gitignore b/.gitignore
new file mode 100755 (executable)
index 0000000..0809663
--- /dev/null
@@ -0,0 +1,14 @@
+/.settings/
+/.project
+/.classpath
+/target/
+/.idea/
+dependency-reduced-pom.xml
+/example/target
+/*.iml
+/JavaInspect.dot
+/JavaInspect.svg
+/doc/index.html
+/doc/usage\ cli.html
+/doc/graphs/
+/doc/apidocs/
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644 (file)
index 0000000..93ead91
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,235 @@
+# JavaInspect - Quick Reference
+
+Utility to visualize Java software structure through reflection and GraphViz.
+
+---
+
+# Quick Lookup: "I Want To..."
+
+| Task                           | Class (path)                               | Key Method                                  |
+|--------------------------------|--------------------------------------------|---------------------------------------------|
+| **Create a graph**             | `ClassGraph` (`structure/ClassGraph.java`) | `new ClassGraph()`                          |
+| **Add classes to graph**       | `ClassGraph`                               | `.add(object)` or `.add(Class)`             |
+| **Generate visualization**     | `ClassGraph`                               | `.generateGraph("name")` → produces SVG/PNG |
+| **Set output directory**       | `ClassGraph`                               | `.setTargetDirectory(new File(path))`       |
+| **Keep DOT file**              | `ClassGraph`                               | `.setKeepDotFile(true)`                     |
+| **Filter classes (blacklist)** | `ClassGraph`                               | `.blacklistClassGlob("java.*")`             |
+| **Filter classes (whitelist)** | `ClassGraph`                               | `.whitelistClassGlob("com.myapp.*")`        |
+| **Hide orphaned classes**      | `ClassGraph`                               | `.hideOrphanedClasses()`                    |
+| **Set output format**          | `ClassGraph`                               | `.setTargetImageType(TargetImageType.SVG)`  |
+| **Run from command line**      | `Main` (`commandline/Main.java`)           | `java -jar javainspect.jar -j myapp.jar`    |
+
+---
+
+# Code Examples
+
+## Basic Usage
+
+```java
+import eu.svjatoslav.inspector.java.structure.ClassGraph;
+
+// Simple one-liner
+new ClassGraph().add(myObject, MyClass.class).generateGraph("MyGraph");
+
+// With configuration
+ClassGraph graph = new ClassGraph();
+graph.add(myObject);               // Add object (class detected automatically)
+graph.add(MyClass.class);          // Add class directly
+graph.setKeepDotFile(true);        // Keep intermediate DOT file
+graph.setTargetDirectory(new File("/tmp"));
+graph.generateGraph("MyApp");      // Generates MyApp.svg
+```
+
+## Filtering with Glob Patterns
+
+```java
+ClassGraph graph = new ClassGraph();
+graph.add(myApp);
+
+// Hide standard library classes
+graph.blacklistClassGlob("java.*");
+graph.blacklistClassGlob("javax.*");
+
+// Only show my package
+graph.whitelistClassGlob("com.mycompany.*");
+
+// Hide test classes
+graph.blacklistClassGlob("*Test");
+
+// Hide classes with no connections
+graph.hideOrphanedClasses();
+
+graph.generateGraph("filtered");
+```
+
+## Analyzing JAR Files from Command Line
+
+```bash
+# Basic usage
+javainspect -j myapp.jar -n MyAppGraph
+
+# Multiple JARs with classpath
+javainspect -j app.jar -j lib.jar -c /path/to/classes -n Combined
+
+# With filters
+javainspect -j myapp.jar -n graph \
+    -b "java.*" -b "*Test" \
+    -w "com.mycompany.*" \
+    -ho  # hide orphaned classes
+
+# Specify output format and directory
+javainspect -j myapp.jar -n graph -t png -d /tmp/graphs
+
+# Keep DOT file for debugging
+javainspect -j myapp.jar -n graph -k
+
+# Debug mode
+javainspect -j myapp.jar -n graph --debug
+```
+
+## Maven Integration
+
+```xml
+<dependency>
+    <groupId>eu.svjatoslav</groupId>
+    <artifactId>javainspect</artifactId>
+    <version>1.8</version>
+</dependency>
+
+<repository>
+    <id>svjatoslav.eu</id>
+    <url>https://www3.svjatoslav.eu/maven/</url>
+</repository>
+```
+
+---
+
+# Class Catalog
+
+## Core Structure (`structure/`)
+
+| Class              | File                    | Purpose                               | Key Methods                                                                                              |
+|--------------------|-------------------------|---------------------------------------|----------------------------------------------------------------------------------------------------------|
+| `ClassGraph`       | `ClassGraph.java`       | Main entry point, graph container     | `.add()`, `.generateGraph()`, `.blacklistClassGlob()`, `.whitelistClassGlob()`, `.hideOrphanedClasses()` |
+| `ClassDescriptor`  | `ClassDescriptor.java`  | Represents a single class node        | `.getDot()`, `.getGraphId()`, `.isVisible()`, `.analyzeClass()`                                          |
+| `FieldDescriptor`  | `FieldDescriptor.java`  | Represents a field within a class     | `.getDot()`, `.getEmbeddedDot()`, `.analyzeField()`                                                      |
+| `MethodDescriptor` | `MethodDescriptor.java` | Represents a method within a class    | `.getDot()`, `.getEmbeddedDot()`, `.analyze()`                                                           |
+| `GraphElement`     | `GraphElement.java`     | Interface for DOT-renderable elements | `.getDot()`, `.getEmbeddedDot()`, `.getGraphId()`, `.isVisible()`                                        |
+| `Utils`            | `Utils.java`            | Filtering helpers, color palettes     | `.isSystemDataType()`, `.isSystemPackage()`, `.isCommonObjectMethod()`, `.getNextDarkColor()`            |
+| `TargetImageType`  | `TargetImageType.java`  | Output format enum                    | `SVG`, `PNG` (field: `.fileExtension`)                                                                   |
+
+## Command Line (`commandline/`)
+
+| Class                      | File                            | Purpose                                |
+|----------------------------|---------------------------------|----------------------------------------|
+| `Main`                     | `Main.java`                     | CLI entry point, JAR processing        |
+| `CommandlineConfiguration` | `CommandlineConfiguration.java` | Argument parsing, option storage       |
+| `TargetImageTypeParameter` | `TargetImageTypeParameter.java` | Custom parameter type for image format |
+
+---
+
+# Architecture
+
+## Graph Building Pipeline
+
+```
+ClassGraph.add(object/class)
+    → getOrCreateClassDescriptor(clazz)
+        → ClassDescriptor.analyzeClass(clazz)
+            → indexFields() → FieldDescriptor.analyzeField()
+            → indexMethods() → MethodDescriptor.analyze()
+            → getOrCreateClassDescriptor(superClass, interfaces)
+    → (recursive for all referenced types)
+
+ClassGraph.generateGraph(name)
+    → getDot() → iterate all ClassDescriptor.getDot()
+    → write DOT file
+    → execute GraphViz (dot -Tsvg)
+    → optionally delete DOT file
+```
+
+## Visibility Filtering
+
+Classes are hidden based on:
+
+1. **System data types**: `void`, `int`, `long`, `boolean`, etc.
+2. **System packages**: `java.*`, `javax.*`, `sun.*`
+3. **Glob blacklist**: classes matching any blacklist pattern
+4. **Glob whitelist**: if defined, only classes matching whitelist shown
+5. **Orphan check**: classes with zero incoming/outgoing references
+
+## DOT Output Structure
+
+Each visible class generates:
+
+1. **Node**: HTML-like table with:
+   - Package name (small font)
+   - Class name (large font)
+   - Fields (type + name rows)
+   - Methods (return type + name rows, red color)
+
+2. **Edges**:
+   - Field → type references (solid arrows)
+   - Method → return type references (dotted arrows)
+   - Interface → implementation (dotted forward arrows)
+   - Superclass → subclass (thick forward arrows)
+
+---
+
+# Build & Test
+
+```bash
+# Build
+mvn clean package
+
+# Run tests
+mvn test
+
+# Generate Javadoc
+mvn javadoc:javadoc
+
+# Run self-visualization demo
+mvn exec:java -Dexec.mainClass="eu.svjatoslav.inspector.java.RenderJavaInspect"
+```
+
+---
+
+# Command Line Options
+
+| Option        | Aliases        | Description                         |
+|---------------|----------------|-------------------------------------|
+| JAR files     | `-j`           | JAR file(s) to analyze              |
+| Classpath     | `-c`           | Additional classpath directories    |
+| Graph name    | `-n`           | Output file name (default: "graph") |
+| Image type    | `-t`           | svg or png (default: svg)           |
+| Target dir    | `-d`           | Output directory (default: current) |
+| Keep DOT      | `-k`           | Keep intermediate DOT file          |
+| Hide orphaned | `-ho`          | Hide classes with no connections    |
+| Whitelist     | `-w`           | Glob patterns to include            |
+| Blacklist     | `-b`           | Glob patterns to exclude            |
+| Root classes  | `-r`           | Specific classes to start from      |
+| Debug         | `--debug`      | Show processing details             |
+| Help          | `-h`, `--help` | Show usage                          |
+
+---
+
+# Tips for AI Agents
+
+1. **ClassGraph is the entry point**: Always start with `new ClassGraph()`
+2. **Method chaining**: `ClassGraph` methods return `this` for chaining
+3. **Glob patterns**: Use `*` for any sequence, `?` for single char
+4. **Reflection-based**: Classes must be loadable in JVM (classpath required)
+5. **GraphViz required**: `dot` binary must be on system PATH
+6. **SVG recommended**: Better for large graphs, PNG for small ones
+7. **Filter early**: Large applications produce huge graphs - use blacklist/whitelist
+8. **Hide orphaned**: Reduces visual noise from disconnected utility classes
+
+---
+
+# Documentation
+
+| Path            | Topic                                                 |
+|-----------------|-------------------------------------------------------|
+| `doc/index.org` | Main documentation, usage examples, Maven integration |
+| `doc/apidocs/`  | Generated Javadoc                                     |
+| `COPYING`       | CC0 license full text                                 |
\ No newline at end of file
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..0e259d4
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,121 @@
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+    HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+  i. the right to reproduce, adapt, distribute, perform, display,
+     communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+     likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+     subject to the limitations in paragraph 4(a), below;
+  v. rights protecting the extraction, dissemination, use and reuse of data
+     in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+     European Parliament and of the Council of 11 March 1996 on the legal
+     protection of databases, and under any national implementation
+     thereof, including any amended or successor version of such
+     directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+     world based on applicable law or treaty, and any national
+     implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+    surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+    warranties of any kind concerning the Work, express, implied,
+    statutory or otherwise, including without limitation warranties of
+    title, merchantability, fitness for a particular purpose, non
+    infringement, or the absence of latent or other defects, accuracy, or
+    the present or absence of errors, whether or not discoverable, all to
+    the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+    that may apply to the Work or any use thereof, including without
+    limitation any person's Copyright and Related Rights in the Work.
+    Further, Affirmer disclaims responsibility for obtaining any necessary
+    consents, permissions or other rights required for any use of the
+    Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+    party to this document and has no duty or obligation with respect to
+    this CC0 or use of the Work.
diff --git a/TODO.org b/TODO.org
new file mode 100644 (file)
index 0000000..3606460
--- /dev/null
+++ b/TODO.org
@@ -0,0 +1,139 @@
+* Bugs
+:PROPERTIES:
+:CUSTOM_ID: bugs
+:END:
+
+** Reference counting should exclude non-visible classes
+:PROPERTIES:
+:CUSTOM_ID: ref-count-exclude-nonvisible
+:END:
+Should not hide references if there are too many of them to classes if
+referring classes are not visible anyway because of blacklist/whitelist
+rules. Basically reference counting should exclude not visible classes.
+
+** Orphaned class removal does not work always
+:PROPERTIES:
+:CUSTOM_ID: orphaned-removal-bugs
+:END:
+There are many bugs and corner cases to find and fix still.
+
+** Code readability needs improvement
+:PROPERTIES:
+:CUSTOM_ID: code-readability
+:END:
+Document and refactor for better maintainability.
+
+* Packaging
+:PROPERTIES:
+:CUSTOM_ID: packaging
+:END:
+
+** Create installable DEB package
+:PROPERTIES:
+:CUSTOM_ID: create-deb-package
+:END:
+Submit it to some Debian developer for integration or become Debian
+package maintainer.
+
+* Architecture
+:PROPERTIES:
+:CUSTOM_ID: architecture
+:END:
+
+** Make modular with central application model
+:PROPERTIES:
+:CUSTOM_ID: modular-architecture
+:END:
+Central part, an application model could be standalone and serializable.
+
+Multiple ways to acquire model:
++ By introspecting application via Java reflections (current mode)
++ By parsing java source (unfinished)
+
+Multiple ways to manipulate model:
++ Store/load/compare
++ Trim uninteresting parts
++ Highlight important parts
+
+Multiple ways to render model:
++ PNG/SVG (currently implemented)
++ PlantUML (TODO)
++ Interactive 3D visualization (TODO)
+
+* Features
+:PROPERTIES:
+:CUSTOM_ID: features
+:END:
+
+** Implement Java source parser
+:PROPERTIES:
+:CUSTOM_ID: java-source-parser
+:END:
+Implement (or integrate existing java parser
+https://javaparser.org/) to be able to produce code visualizations
+based on source code (in addition to current reflection based approach).
+
+** Integrate with PlantUML
+:PROPERTIES:
+:CUSTOM_ID: plantuml-integration
+:END:
+http://plantuml.com/class-diagram
+
+** Add dark theme for generated graphs
+:PROPERTIES:
+:CUSTOM_ID: dark-theme
+:END:
+
+** Sort class fields alphabetically
+:PROPERTIES:
+:CUSTOM_ID: sort-fields-alphabetically
+:END:
+
+** Visualize concrete field values
+:PROPERTIES:
+:CUSTOM_ID: visualize-field-values
+:END:
+Could be used as ultra cool runtime logging/debugging framework.
+
+** Visualize from JVM snapshot
+:PROPERTIES:
+:CUSTOM_ID: jvm-snapshot
+:END:
+
+** Attach to remote process via JVM debug port
+:PROPERTIES:
+:CUSTOM_ID: remote-process-debug
+:END:
+
+** Attach to JVM using JVM agent
+:PROPERTIES:
+:CUSTOM_ID: jvm-agent
+:END:
+
+** 3D visualization with Sixth 3D engine
+:PROPERTIES:
+:CUSTOM_ID: 3d-visualization
+:END:
+https://www3.svjatoslav.eu/projects/sixth-3d/
+
+** Add graph query language
+:PROPERTIES:
+:CUSTOM_ID: graph-query-language
+:END:
+Select classes/fields/values to be visualized in some graph query
+language. For greater flexibility in comparison to currently supported
+glob syntax.
+
+** Add JSON/XML config file support
+:PROPERTIES:
+:CUSTOM_ID: config-file
+:END:
+Different graphs for given project could be defined once in plain text
+config, possibly with the aid of some interactive utility. Then defined
+graphs could be updated as part of project build or release process.
+
+** Configurable Maven plugin
+:PROPERTIES:
+:CUSTOM_ID: maven-plugin
+:END:
+Generate graphs as part of the project build/release process.
\ No newline at end of file
diff --git a/Tools/Open with IntelliJ IDEA b/Tools/Open with IntelliJ IDEA
new file mode 100755 (executable)
index 0000000..de9bae5
--- /dev/null
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+#
+# This is a helper bash script that starts IntelliJ with the current project.
+# Script is written is such a way that you can simply click on it in file
+# navigator to run it.
+#
+#
+# Script assumes:
+#
+#    + GNU operating system
+#    + IntelliJ is installed and commandline launcher "idea" is enabled.
+#
+
+cd "${0%/*}"
+cd ..
+
+setsid idea . &>/dev/null
diff --git a/Tools/Update web site b/Tools/Update web site
new file mode 100755 (executable)
index 0000000..86d62a0
--- /dev/null
@@ -0,0 +1,127 @@
+#!/bin/bash
+cd "${0%/*}"; if [ "$1" != "T" ]; then gnome-terminal -- "$0" T; exit; fi;
+
+cd ..
+
+# Function to export org to html using emacs in batch mode
+export_org_to_html() {
+    local org_file=$1
+    local dir=$(dirname "$org_file")
+    local base=$(basename "$org_file" .org)
+    (
+        cd "$dir" || return 1
+        local html_file="${base}.html"
+        if [ -f "$html_file" ]; then
+            rm -f "$html_file"
+        fi
+        echo "Exporting: $org_file → $dir/$html_file"
+        emacs --batch -l ~/.emacs --visit="${base}.org" --funcall=org-html-export-to-html --kill
+        if [ $? -eq 0 ]; then
+            echo "✓ Successfully exported $org_file"
+        else
+            echo "✗ Failed to export $org_file"
+            return 1
+        fi
+    )
+}
+
+export_org_files_to_html() {
+    echo "🔍 Searching for .org files in doc/ ..."
+    echo "======================================="
+
+    mapfile -t ORG_FILES < <(find doc -type f -name "*.org" | sort)
+
+    if [ ${#ORG_FILES[@]} -eq 0 ]; then
+        echo "❌ No .org files found!"
+        return 1
+    fi
+
+    echo "Found ${#ORG_FILES[@]} .org file(s):"
+    printf '%s\n' "${ORG_FILES[@]}"
+    echo "======================================="
+
+    SUCCESS_COUNT=0
+    FAILED_COUNT=0
+
+    for org_file in "${ORG_FILES[@]}"; do
+        export_org_to_html "$org_file"
+        if [ $? -eq 0 ]; then
+            ((SUCCESS_COUNT++))
+        else
+            ((FAILED_COUNT++))
+        fi
+    done
+
+    echo "======================================="
+    echo "📊 SUMMARY:"
+    echo "   ✓ Successful: $SUCCESS_COUNT"
+    echo "   ✗ Failed:     $FAILED_COUNT"
+    echo "   Total:        $((SUCCESS_COUNT + FAILED_COUNT))"
+    echo ""
+}
+
+build_visualization_graphs() {
+    echo "🎨 Generating class visualization graphs..."
+    echo "======================================="
+
+    rm -rf doc/graphs/
+    mkdir -p doc/graphs/
+
+    # JavaInspect analyzing itself - meta visualization!
+    javainspect -j target/javainspect.jar -d doc/graphs/ -n "All classes" -t png
+    javainspect -j target/javainspect.jar -d doc/graphs/ -n "Inspector" -t png -w "eu.svjatoslav.inspector.*"
+    javainspect -j target/javainspect.jar -d doc/graphs/ -n "Java parser" -t png -w "eu.svjatoslav.inspector.java.*"
+
+    # Generate index page for graphs
+    meviz index -w doc/graphs/ -t "JavaInspect classes"
+
+    echo "✓ Visualization graphs generated"
+    echo ""
+}
+
+# Build project jar file and JavaDocs
+echo "🔨 Building project..."
+echo "======================================="
+mvn clean package
+
+if [ $? -ne 0 ]; then
+    echo "✗ Maven build failed!"
+    echo ""
+    echo "Press ENTER to close this window."
+    read
+    exit 1
+fi
+
+echo "✓ Maven build completed"
+echo ""
+
+# Put generated JavaDoc HTML files to documentation directory
+echo "📚 Copying JavaDocs..."
+echo "======================================="
+rm -rf doc/apidocs/
+cp -r target/apidocs/ doc/
+
+echo "✓ JavaDocs copied to doc/apidocs/"
+echo ""
+
+# Publish Emacs org-mode files into HTML format
+export_org_files_to_html
+
+# Generate nice looking code visualization diagrams
+build_visualization_graphs
+
+## Upload assembled documentation to server
+echo "📤 Uploading to server..."
+echo "======================================="
+rsync -avz --delete -e 'ssh -p 10006' doc/ \
+      n0@www3.svjatoslav.eu:/mnt/big/projects/javainspect/
+
+if [ $? -eq 0 ]; then
+    echo "✓ Upload completed successfully!"
+else
+    echo "✗ Upload failed!"
+fi
+
+echo ""
+echo "Press ENTER to close this window."
+read
\ No newline at end of file
diff --git a/commandline launcher/install b/commandline launcher/install
new file mode 100755 (executable)
index 0000000..b7c7b3e
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# This script installs JavaInspect into /opt/javainspect/ directory.
+# It also enables javainspect command at the commandline.
+#
+
+(
+    # go to project root directory
+    cd ..
+
+    # remove old installation (if any)
+    sudo rm -rf /opt/javainspect/
+
+    # compile new package
+    mvn clean package
+
+    # create installation directory
+    sudo mkdir -p /opt/javainspect/
+
+    # copy javainspect jar file to opt installation directory
+    sudo cp target/javainspect.jar /opt/javainspect/
+)
+
+# deploy launcher script to /usr/local/bin/javainspect
+sudo cp javainspect /usr/local/bin/
diff --git a/commandline launcher/javainspect b/commandline launcher/javainspect
new file mode 100755 (executable)
index 0000000..62971e5
--- /dev/null
@@ -0,0 +1,7 @@
+#!/bin/bash
+#
+# Launcher script for JavaInspect utility:
+#    https://www3.svjatoslav.eu/projects/javainspect/
+#
+
+java -jar /opt/javainspect/javainspect.jar "$@"
diff --git a/doc/JavaInspect full project.png b/doc/JavaInspect full project.png
new file mode 100644 (file)
index 0000000..4d5ebba
Binary files /dev/null and b/doc/JavaInspect full project.png differ
diff --git a/doc/JavaInspect.dot b/doc/JavaInspect.dot
new file mode 100644 (file)
index 0000000..660c81c
--- /dev/null
@@ -0,0 +1,316 @@
+digraph Java {
+graph [rankdir=LR, overlap = false, concentrate=true];
+
+// Class: eu.svjatoslav.inspector.java.structure.Filter
+    class_eu_svjatoslav_inspector_java_structure_Filter[label=<<TABLE  BORDER="1" CELLBORDER="1" CELLSPACING="0">
+
+    // class descriptor header
+    <TR><TD colspan="2" PORT="f0"><FONT POINT-SIZE="8.0" >eu.svjatoslav.inspector.java.structure</FONT><br/><FONT POINT-SIZE="25.0"><B>Filter</B></FONT></TD></TR>
+
+    // fields:
+        // blacklistClassPatterns
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">List</FONT></td><TD PORT="blacklistClassPatterns" ALIGN="left"><FONT POINT-SIZE="11.0">blacklistClassPatterns</FONT></TD></TR>
+        // whitelistClassPatterns
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">List</FONT></td><TD PORT="whitelistClassPatterns" ALIGN="left"><FONT POINT-SIZE="11.0">whitelistClassPatterns</FONT></TD></TR>
+
+    // methods:
+        // blacklistClassPattern
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">void</FONT></td><TD PORT="blacklistClassPattern" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">blacklistClassPattern</FONT></TD></TR>
+        // isClassShown
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">boolean</FONT></td><TD PORT="isClassShown" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">isClassShown</FONT></TD></TR>
+        // whitelistClassPattern
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">void</FONT></td><TD PORT="whitelistClassPattern" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">whitelistClassPattern</FONT></TD></TR>
+    </TABLE>>, shape="none"];
+
+    // field references to other classes
+
+    // method references to other classes
+
+// Class: eu.svjatoslav.inspector.java.structure.FieldDescriptor
+    class_eu_svjatoslav_inspector_java_structure_FieldDescriptor[label=<<TABLE  BORDER="1" CELLBORDER="1" CELLSPACING="0">
+
+    // class descriptor header
+    <TR><TD colspan="2" PORT="f0"><FONT POINT-SIZE="8.0" >eu.svjatoslav.inspector.java.structure</FONT><br/><FONT POINT-SIZE="25.0"><B>FieldDescriptor</B></FONT></TD></TR>
+
+    // fields:
+        // name
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">String</FONT></td><TD PORT="name" ALIGN="left"><FONT POINT-SIZE="11.0">name</FONT></TD></TR>
+        // parent
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">ClassDescriptor</FONT></td><TD PORT="parent" ALIGN="left"><FONT POINT-SIZE="11.0">parent</FONT></TD></TR>
+        // type
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">ClassDescriptor</FONT></td><TD PORT="type" ALIGN="left"><FONT POINT-SIZE="11.0">type</FONT></TD></TR>
+        // typeArguments
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">List</FONT></td><TD PORT="typeArguments" ALIGN="left"><FONT POINT-SIZE="11.0">typeArguments</FONT></TD></TR>
+
+    // methods:
+        // getDot
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">String</FONT></td><TD PORT="getDot" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">getDot</FONT></TD></TR>
+        // getEmbeddedDot
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">String</FONT></td><TD PORT="getEmbeddedDot" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">getEmbeddedDot</FONT></TD></TR>
+        // getGraphId
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">String</FONT></td><TD PORT="getGraphId" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">getGraphId</FONT></TD></TR>
+        // getOutsideVisibleReferencesCount
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">int</FONT></td><TD PORT="getOutsideVisibleReferencesCount" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">getOutsideVisibleReferencesCount</FONT></TD></TR>
+        // isVisible
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">boolean</FONT></td><TD PORT="isVisible" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">isVisible</FONT></TD></TR>
+    </TABLE>>, shape="none"];
+
+    // field references to other classes
+
+    // method references to other classes
+
+    // interfaces implemented by class: eu.svjatoslav.inspector.java.structure.FieldDescriptor
+    class_eu_svjatoslav_inspector_java_structure_GraphElement -> class_eu_svjatoslav_inspector_java_structure_FieldDescriptor[style="dotted, tapered", color="olivedrab2", penwidth=20, dir="forward"];
+
+// Class: eu.svjatoslav.inspector.java.structure.MethodDescriptor
+    class_eu_svjatoslav_inspector_java_structure_MethodDescriptor[label=<<TABLE  BORDER="1" CELLBORDER="1" CELLSPACING="0">
+
+    // class descriptor header
+    <TR><TD colspan="2" PORT="f0"><FONT POINT-SIZE="8.0" >eu.svjatoslav.inspector.java.structure</FONT><br/><FONT POINT-SIZE="25.0"><B>MethodDescriptor</B></FONT></TD></TR>
+
+    // fields:
+        // name
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">String</FONT></td><TD PORT="name" ALIGN="left"><FONT POINT-SIZE="11.0">name</FONT></TD></TR>
+        // parent
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">ClassDescriptor</FONT></td><TD PORT="parent" ALIGN="left"><FONT POINT-SIZE="11.0">parent</FONT></TD></TR>
+        // returnType
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">ClassDescriptor</FONT></td><TD PORT="returnType" ALIGN="left"><FONT POINT-SIZE="11.0">returnType</FONT></TD></TR>
+        // typeArguments
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">List</FONT></td><TD PORT="typeArguments" ALIGN="left"><FONT POINT-SIZE="11.0">typeArguments</FONT></TD></TR>
+
+    // methods:
+        // compareTo
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">int</FONT></td><TD PORT="compareTo" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">compareTo</FONT></TD></TR>
+        // compareTo
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">int</FONT></td><TD PORT="compareTo" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">compareTo</FONT></TD></TR>
+        // getDot
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">String</FONT></td><TD PORT="getDot" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">getDot</FONT></TD></TR>
+        // getEmbeddedDot
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">String</FONT></td><TD PORT="getEmbeddedDot" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">getEmbeddedDot</FONT></TD></TR>
+        // getGraphId
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">String</FONT></td><TD PORT="getGraphId" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">getGraphId</FONT></TD></TR>
+        // getMethodLabel
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">String</FONT></td><TD PORT="getMethodLabel" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">getMethodLabel</FONT></TD></TR>
+        // getOutsideVisibleReferencesCount
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">int</FONT></td><TD PORT="getOutsideVisibleReferencesCount" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">getOutsideVisibleReferencesCount</FONT></TD></TR>
+        // isVisible
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">boolean</FONT></td><TD PORT="isVisible" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">isVisible</FONT></TD></TR>
+    </TABLE>>, shape="none"];
+
+    // field references to other classes
+
+    // method references to other classes
+
+    // interfaces implemented by class: eu.svjatoslav.inspector.java.structure.MethodDescriptor
+    class_eu_svjatoslav_inspector_java_structure_GraphElement -> class_eu_svjatoslav_inspector_java_structure_MethodDescriptor[style="dotted, tapered", color="olivedrab2", penwidth=20, dir="forward"];
+
+// Class: eu.svjatoslav.inspector.java.structure.Utils
+    class_eu_svjatoslav_inspector_java_structure_Utils[label=<<TABLE  BORDER="1" CELLBORDER="1" CELLSPACING="0">
+
+    // class descriptor header
+    <TR><TD colspan="2" PORT="f0"><FONT POINT-SIZE="8.0" >eu.svjatoslav.inspector.java.structure</FONT><br/><FONT POINT-SIZE="25.0"><B>Utils</B></FONT></TD></TR>
+
+    // fields:
+        // darkColors
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">List</FONT></td><TD PORT="darkColors" ALIGN="left"><FONT POINT-SIZE="11.0">darkColors</FONT></TD></TR>
+        // enumMethods
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">List</FONT></td><TD PORT="enumMethods" ALIGN="left"><FONT POINT-SIZE="11.0">enumMethods</FONT></TD></TR>
+        // lastChosenDarkColor
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">int</FONT></td><TD PORT="lastChosenDarkColor" ALIGN="left"><FONT POINT-SIZE="11.0">lastChosenDarkColor</FONT></TD></TR>
+        // lastChosenLightColor
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">int</FONT></td><TD PORT="lastChosenLightColor" ALIGN="left"><FONT POINT-SIZE="11.0">lastChosenLightColor</FONT></TD></TR>
+        // lightColors
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">List</FONT></td><TD PORT="lightColors" ALIGN="left"><FONT POINT-SIZE="11.0">lightColors</FONT></TD></TR>
+        // systemDataTypes
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">List</FONT></td><TD PORT="systemDataTypes" ALIGN="left"><FONT POINT-SIZE="11.0">systemDataTypes</FONT></TD></TR>
+        // systemMethods
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">List</FONT></td><TD PORT="systemMethods" ALIGN="left"><FONT POINT-SIZE="11.0">systemMethods</FONT></TD></TR>
+        // systemPackages
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">List</FONT></td><TD PORT="systemPackages" ALIGN="left"><FONT POINT-SIZE="11.0">systemPackages</FONT></TD></TR>
+
+    // methods:
+        // getNextDarkColor
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">String</FONT></td><TD PORT="getNextDarkColor" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">getNextDarkColor</FONT></TD></TR>
+        // getNextLightColor
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">String</FONT></td><TD PORT="getNextLightColor" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">getNextLightColor</FONT></TD></TR>
+        // initDarkColors
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">void</FONT></td><TD PORT="initDarkColors" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">initDarkColors</FONT></TD></TR>
+        // initEnumMethods
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">void</FONT></td><TD PORT="initEnumMethods" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">initEnumMethods</FONT></TD></TR>
+        // initLightColors
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">void</FONT></td><TD PORT="initLightColors" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">initLightColors</FONT></TD></TR>
+        // initSystemDataTypes
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">void</FONT></td><TD PORT="initSystemDataTypes" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">initSystemDataTypes</FONT></TD></TR>
+        // initSystemMethods
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">void</FONT></td><TD PORT="initSystemMethods" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">initSystemMethods</FONT></TD></TR>
+        // initSystemPackages
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">void</FONT></td><TD PORT="initSystemPackages" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">initSystemPackages</FONT></TD></TR>
+        // isEnumMethod
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">boolean</FONT></td><TD PORT="isEnumMethod" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">isEnumMethod</FONT></TD></TR>
+        // isSystemDataType
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">boolean</FONT></td><TD PORT="isSystemDataType" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">isSystemDataType</FONT></TD></TR>
+        // isSystemMethod
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">boolean</FONT></td><TD PORT="isSystemMethod" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">isSystemMethod</FONT></TD></TR>
+        // isSystemPackage
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">boolean</FONT></td><TD PORT="isSystemPackage" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">isSystemPackage</FONT></TD></TR>
+    </TABLE>>, shape="none"];
+
+    // field references to other classes
+
+    // method references to other classes
+
+// Class: eu.svjatoslav.inspector.java.structure.GraphElement
+    class_eu_svjatoslav_inspector_java_structure_GraphElement[label=<<TABLE bgcolor="darkslategray1" BORDER="1" CELLBORDER="1" CELLSPACING="0">
+
+    // class descriptor header
+    <TR><TD colspan="2" PORT="f0"><FONT POINT-SIZE="8.0" >eu.svjatoslav.inspector.java.structure</FONT><br/><FONT POINT-SIZE="25.0"><B>GraphElement</B></FONT></TD></TR>
+
+    // methods:
+        // getDot
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">String</FONT></td><TD PORT="getDot" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">getDot</FONT></TD></TR>
+        // getEmbeddedDot
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">String</FONT></td><TD PORT="getEmbeddedDot" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">getEmbeddedDot</FONT></TD></TR>
+        // getGraphId
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">String</FONT></td><TD PORT="getGraphId" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">getGraphId</FONT></TD></TR>
+        // isVisible
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">boolean</FONT></td><TD PORT="isVisible" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">isVisible</FONT></TD></TR>
+    </TABLE>>, shape="none"];
+
+    // method references to other classes
+
+// Class: eu.svjatoslav.inspector.java.structure.ClassGraph
+    class_eu_svjatoslav_inspector_java_structure_ClassGraph[label=<<TABLE  BORDER="1" CELLBORDER="1" CELLSPACING="0">
+
+    // class descriptor header
+    <TR><TD colspan="2" PORT="f0"><FONT POINT-SIZE="8.0" >eu.svjatoslav.inspector.java.structure</FONT><br/><FONT POINT-SIZE="25.0"><B>ClassGraph</B></FONT></TD></TR>
+
+    // fields:
+        // filter
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">Filter</FONT></td><TD PORT="filter" ALIGN="left"><FONT POINT-SIZE="11.0">filter</FONT></TD></TR>
+        // nameToClassMap
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">Map</FONT></td><TD PORT="nameToClassMap" ALIGN="left"><FONT POINT-SIZE="11.0">nameToClassMap</FONT></TD></TR>
+
+    // methods:
+        // addClass
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">ClassDescriptor</FONT></td><TD PORT="addClass" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">addClass</FONT></TD></TR>
+        // addObject
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">ClassDescriptor</FONT></td><TD PORT="addObject" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">addObject</FONT></TD></TR>
+        // addProject
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">void</FONT></td><TD PORT="addProject" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">addProject</FONT></TD></TR>
+        // generateGraph
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">void</FONT></td><TD PORT="generateGraph" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">generateGraph</FONT></TD></TR>
+        // generateGraph
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">void</FONT></td><TD PORT="generateGraph" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">generateGraph</FONT></TD></TR>
+        // hideOrphanedClasses
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">void</FONT></td><TD PORT="hideOrphanedClasses" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">hideOrphanedClasses</FONT></TD></TR>
+        // render
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">void</FONT></td><TD PORT="render" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">render</FONT></TD></TR>
+    </TABLE>>, shape="none"];
+
+    // field references to other classes
+    class_eu_svjatoslav_inspector_java_structure_ClassGraph:filter -> class_eu_svjatoslav_inspector_java_structure_Filter[label="filter", color="antiquewhite4", style="bold"];
+
+    // method references to other classes
+
+// Class: eu.svjatoslav.inspector.java.structure.ClassDescriptor
+    class_eu_svjatoslav_inspector_java_structure_ClassDescriptor[label=<<TABLE  BORDER="4" CELLBORDER="1" CELLSPACING="0">
+
+    // class descriptor header
+    <TR><TD colspan="2" PORT="f0"><FONT POINT-SIZE="8.0" >eu.svjatoslav.inspector.java.structure</FONT><br/><FONT POINT-SIZE="25.0"><B>ClassDescriptor</B></FONT></TD></TR>
+
+    // fields:
+        // MAX_REFERECNES_COUNT
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">int</FONT></td><TD PORT="MAX_REFERECNES_COUNT" ALIGN="left"><FONT POINT-SIZE="11.0">MAX_REFERECNES_COUNT</FONT></TD></TR>
+        // classGraph
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">ClassGraph</FONT></td><TD PORT="classGraph" ALIGN="left"><FONT POINT-SIZE="11.0">classGraph</FONT></TD></TR>
+        // distinctiveReferenceColor
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">String</FONT></td><TD PORT="distinctiveReferenceColor" ALIGN="left"><FONT POINT-SIZE="11.0">distinctiveReferenceColor</FONT></TD></TR>
+        // fullyQualifiedName
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">String</FONT></td><TD PORT="fullyQualifiedName" ALIGN="left"><FONT POINT-SIZE="11.0">fullyQualifiedName</FONT></TD></TR>
+        // incomingReferencesCount
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">int</FONT></td><TD PORT="incomingReferencesCount" ALIGN="left"><FONT POINT-SIZE="11.0">incomingReferencesCount</FONT></TD></TR>
+        // interfaceColor
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">String</FONT></td><TD PORT="interfaceColor" ALIGN="left"><FONT POINT-SIZE="11.0">interfaceColor</FONT></TD></TR>
+        // interfaces
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">List</FONT></td><TD PORT="interfaces" ALIGN="left"><FONT POINT-SIZE="11.0">interfaces</FONT></TD></TR>
+        // isArray
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">boolean</FONT></td><TD PORT="isArray" ALIGN="left"><FONT POINT-SIZE="11.0">isArray</FONT></TD></TR>
+        // isEnum
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">boolean</FONT></td><TD PORT="isEnum" ALIGN="left"><FONT POINT-SIZE="11.0">isEnum</FONT></TD></TR>
+        // isInterface
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">boolean</FONT></td><TD PORT="isInterface" ALIGN="left"><FONT POINT-SIZE="11.0">isInterface</FONT></TD></TR>
+        // isShown
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">boolean</FONT></td><TD PORT="isShown" ALIGN="left"><FONT POINT-SIZE="11.0">isShown</FONT></TD></TR>
+        // methods
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">SortedSet</FONT></td><TD PORT="methods" ALIGN="left"><FONT POINT-SIZE="11.0">methods</FONT></TD></TR>
+        // nameToFieldMap
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">Map</FONT></td><TD PORT="nameToFieldMap" ALIGN="left"><FONT POINT-SIZE="11.0">nameToFieldMap</FONT></TD></TR>
+        // superClass
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">ClassDescriptor</FONT></td><TD PORT="superClass" ALIGN="left"><FONT POINT-SIZE="11.0">superClass</FONT></TD></TR>
+        // superClassColor
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">String</FONT></td><TD PORT="superClassColor" ALIGN="left"><FONT POINT-SIZE="11.0">superClassColor</FONT></TD></TR>
+
+    // methods:
+        // areReferencesShown
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">boolean</FONT></td><TD PORT="areReferencesShown" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">areReferencesShown</FONT></TD></TR>
+        // enlistFieldReferences
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">void</FONT></td><TD PORT="enlistFieldReferences" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">enlistFieldReferences</FONT></TD></TR>
+        // enlistFields
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">void</FONT></td><TD PORT="enlistFields" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">enlistFields</FONT></TD></TR>
+        // enlistImplementedInterfaces
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">void</FONT></td><TD PORT="enlistImplementedInterfaces" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">enlistImplementedInterfaces</FONT></TD></TR>
+        // enlistMethodReferences
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">void</FONT></td><TD PORT="enlistMethodReferences" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">enlistMethodReferences</FONT></TD></TR>
+        // enlistMethods
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">void</FONT></td><TD PORT="enlistMethods" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">enlistMethods</FONT></TD></TR>
+        // enlistSuperClass
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">void</FONT></td><TD PORT="enlistSuperClass" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">enlistSuperClass</FONT></TD></TR>
+        // generateDotHeader
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">void</FONT></td><TD PORT="generateDotHeader" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">generateDotHeader</FONT></TD></TR>
+        // getAllFields
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">List</FONT></td><TD PORT="getAllFields" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">getAllFields</FONT></TD></TR>
+        // getBackgroundColor
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">String</FONT></td><TD PORT="getBackgroundColor" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">getBackgroundColor</FONT></TD></TR>
+        // getBorderWidth
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">String</FONT></td><TD PORT="getBorderWidth" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">getBorderWidth</FONT></TD></TR>
+        // getClassName
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">String</FONT></td><TD PORT="getClassName" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">getClassName</FONT></TD></TR>
+        // getColor
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">String</FONT></td><TD PORT="getColor" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">getColor</FONT></TD></TR>
+        // getDistinctiveColor
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">String</FONT></td><TD PORT="getDistinctiveColor" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">getDistinctiveColor</FONT></TD></TR>
+        // getDot
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">String</FONT></td><TD PORT="getDot" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">getDot</FONT></TD></TR>
+        // getEmbeddedDot
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">String</FONT></td><TD PORT="getEmbeddedDot" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">getEmbeddedDot</FONT></TD></TR>
+        // getGraphId
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">String</FONT></td><TD PORT="getGraphId" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">getGraphId</FONT></TD></TR>
+        // getPackageName
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">String</FONT></td><TD PORT="getPackageName" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">getPackageName</FONT></TD></TR>
+        // getParentClassesName
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">String</FONT></td><TD PORT="getParentClassesName" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">getParentClassesName</FONT></TD></TR>
+        // hide
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">void</FONT></td><TD PORT="hide" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">hide</FONT></TD></TR>
+        // hideClassIfNoReferences
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">boolean</FONT></td><TD PORT="hideClassIfNoReferences" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">hideClassIfNoReferences</FONT></TD></TR>
+        // indexFields
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">void</FONT></td><TD PORT="indexFields" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">indexFields</FONT></TD></TR>
+        // isVisible
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">boolean</FONT></td><TD PORT="isVisible" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">isVisible</FONT></TD></TR>
+        // registerReference
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">void</FONT></td><TD PORT="registerReference" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">registerReference</FONT></TD></TR>
+        // setDistinctiveColor
+        <TR><td ALIGN="right"><FONT POINT-SIZE="8.0">void</FONT></td><TD PORT="setDistinctiveColor" ALIGN="left"><FONT COLOR ="red" POINT-SIZE="11.0">setDistinctiveColor</FONT></TD></TR>
+    </TABLE>>, shape="none"];
+
+    // field references to other classes
+    class_eu_svjatoslav_inspector_java_structure_ClassDescriptor:classGraph -> class_eu_svjatoslav_inspector_java_structure_ClassGraph[label="classGraph", color="blueviolet", style="bold"];
+    class_eu_svjatoslav_inspector_java_structure_ClassDescriptor:methods -> class_eu_svjatoslav_inspector_java_structure_MethodDescriptor[label="methods", color="brown4", style="bold"];
+    class_eu_svjatoslav_inspector_java_structure_ClassDescriptor:nameToFieldMap -> class_eu_svjatoslav_inspector_java_structure_FieldDescriptor[label="nameToFieldMap", color="chartreuse4", style="bold"];
+
+    // method references to other classes
+    class_eu_svjatoslav_inspector_java_structure_ClassDescriptor:getAllFields -> class_eu_svjatoslav_inspector_java_structure_FieldDescriptor[label="getAllFields", color="chartreuse4", style="dotted, bold"];
+
+    // interfaces implemented by class: eu.svjatoslav.inspector.java.structure.ClassDescriptor
+    class_eu_svjatoslav_inspector_java_structure_GraphElement -> class_eu_svjatoslav_inspector_java_structure_ClassDescriptor[style="dotted, tapered", color="olivedrab2", penwidth=20, dir="forward"];
+}
diff --git a/doc/JavaInspect.png b/doc/JavaInspect.png
new file mode 100644 (file)
index 0000000..625a2da
Binary files /dev/null and b/doc/JavaInspect.png differ
diff --git a/doc/example-thumbnail.png b/doc/example-thumbnail.png
new file mode 100644 (file)
index 0000000..04f4195
Binary files /dev/null and b/doc/example-thumbnail.png differ
diff --git a/doc/example.png b/doc/example.png
new file mode 100644 (file)
index 0000000..d773c31
Binary files /dev/null and b/doc/example.png differ
diff --git a/doc/index.org b/doc/index.org
new file mode 100644 (file)
index 0000000..145282f
--- /dev/null
@@ -0,0 +1,237 @@
+#+SETUPFILE: ~/.emacs.d/org-styles/html/darksun.theme
+#+TITLE: JavaInspect - Utility to visualize java software
+#+LANGUAGE: en
+#+LATEX_HEADER: \usepackage[margin=1.0in]{geometry}
+#+LATEX_HEADER: \usepackage{parskip}
+#+LATEX_HEADER: \usepackage[none]{hyphenat}
+
+#+OPTIONS: H:20 num:20
+#+OPTIONS: author:nil
+
+* Introduction
+:PROPERTIES:
+:CUSTOM_ID: overview
+:END:
+
+*JavaInspect* utility simplifies understanding the computer program
+code by automatically visualizing its structure.
+
+[[https://www3.svjatoslav.eu/projects/sixth-3d/graphs/][See example produced graphs]] for [[https://www3.svjatoslav.eu/projects/sixth-3d/][Sixth 3D - 3D engine project]].
+
+JavaInspect can be used as a [[id:acf1896a-74b4-4914-acf6-a77075e07f25][standalone commandline utility]] as well as
+[[id:bbeeffc8-3767-440d-8d93-ec9124dd60ee][java library]]. JavaInspect uses primarily Java built-in reflection to
+discover and visualize any part of Java program.
+
+JavaInspect currently has no graphical user interface, configuration
+files, embedded scripting support, direct Maven, Gradle or Ant
+integration. See [[id:2ad2889e-6c95-4662-b3f4-2c341fc74522][usage]] to learn how to instuct Javainspect what to do.
+
+After discovering application structure and optionally filtering out
+unimportant parts, JavaInspect produces GraphViz dot file that
+describes data to be visualized. Then launches [[https://graphviz.org/][GraphViz]] to generate
+bitmap graph in PNG or SVG format.
+
+Notes:
++ JavaInspect is developed and tested so far only on GNU/Linux.
+
+
+*A very simple example:*
+
+[[file:example.png][file:example-thumbnail.png]]
+
+Graph legend:
+file:legend.png
+
+
++ [[https://www3.svjatoslav.eu/projects/sixth-3d/graphs/][See example produced graphs]] for [[https://www3.svjatoslav.eu/projects/sixth-3d/][Sixth 3D - 3D engine project]].
+
+** See also
+:PROPERTIES:
+:CUSTOM_ID: see-also
+:END:
+
+Similar or alternative solutions:
++ http://www.class-visualizer.net/
++ [[https://github.com/pahen/madge][Madge - similar tool for JavaScript]]
+
+* Installation
+:PROPERTIES:
+:CUSTOM_ID: installation
+:END:
+[[http://www.graphviz.org/][GraphViz]] - shall be installed on the computer.
+
+On Ubuntu/Debian GraphViz can be installed using:
+#+BEGIN_SRC sh
+sudo apt-get install graphviz
+#+END_SRC
+
+To use JavaInspect via Java API, no further installation is
+needed. JavaInspect will be embedded into your project as dependency.
+This is described in [[id:bbeeffc8-3767-440d-8d93-ec9124dd60ee][usage via Java API]]. It will expect GraphViz to be
+available in the system.
+
+To use JavaInspect as a commandline tool, JavaInspect source
+repository has to be cloned locally. See: [[id:c47ff9a6-d737-4b73-9393-1c63d2ca1101][Getting the source code]].
+
+Then study and execute installation script:
+#+begin_src sh
+  cd commandline\ launcher
+  ./install
+#+end_src
+
+After installation, new commandline tool should be available
+: javainspect
+
+Quick commandline usage help can be viewed by issuing
+: javainspect --help
+
+* Usage
+:PROPERTIES:
+:ID:       2ad2889e-6c95-4662-b3f4-2c341fc74522
+:CUSTOM_ID: usage
+:END:
+JavaInspect utility can be used in 2 different ways:
++ [[id:acf1896a-74b4-4914-acf6-a77075e07f25][As standalone commandline utility]]
++ [[id:bbeeffc8-3767-440d-8d93-ec9124dd60ee][As embedded Java library via Java API]]
+
+** Usage via Java API
+:PROPERTIES:
+:ID:       bbeeffc8-3767-440d-8d93-ec9124dd60ee
+:CUSTOM_ID: usage-via-java-api
+:END:
+Requires that classes to be visualised are available in the classpath.
+
+To get JavaInspect into same classpath with your projecs I so far came
+up with 2 solutions:
+
+1. Add JavaInspect library in your project as a dependency.
+
+2. Create new Java project for the purpose visualizing your other
+   projects and include JavaInspect and your projecs binary artifacts
+   (Jar's) into new project classpath. Built binary Jar's (with no
+   source code) are sufficient because JavaInspect operates via
+   reflection.
+
+Simple Java based control/configuration code needs to be written for
+each project. I usually put such code into directories devoted for
+JUnit tests. Because it needs not to be compiled/embedded into final
+product or project artifact I'm just willing to visualize.
+
+Control code in general does the following:
+1. Create graph object.
+2. Java reflection/classloaders does not provide mechanism for
+   discovering all classes under given package. Therefore you need to
+   declare at least some classes to be added to the graph by manually
+   adding individual classes to the graph. For every class added to
+   the graph, GraphViz will recursively inspect it and add all
+   referecned classes to the graph as well.
+3. Graphs easilly get very big and complex so optionally we filter
+   important code using classname [[https://en.wikipedia.org/wiki/Glob_(programming)][glob]] patterns based blacklist and/or
+   whitelist.
+4. Optionally we can tune some rendering parameters like:
+   + Possibility to remove orphaned classes (classes with no
+     references) from the graph.
+   + Specify target directory for generated visualization
+     files. (Default is current directory)
+   + Keep intermediate GraphViz dot file for later inspection.
+5. Render graph.
+
+
+*** Example 1: individually picked objects
+:PROPERTIES:
+:CUSTOM_ID: example-1-individually-picked-objects
+:END:
+This example demonstrates generating of class graph from hand picked
+classes and visualizing GraphViz itself.
+
+#+BEGIN_SRC java
+
+// Create graph
+final ClassGraph graph = new ClassGraph();
+
+// Add some random object to the graph. GraphViz will detect Class from
+// the object.
+graph.add(graph);
+
+// Also add some random class to the graph.
+graph.add(Utils.class);
+
+// Keep intermediary GraphViz DOT file for reference.
+graph.setKeepDotFile(true);
+
+// Produce bitmap image titled "JavaInspect.png" to the user Desktop
+// directory
+graph.generateGraph("JavaInspect");
+
+#+END_SRC
+
+Note: if desired, more compact version of the above:
+#+BEGIN_SRC java
+new ClassGraph().add(randomObject, RandomClass.class)
+                .setKeepDotFile(true).generateGraph("JavaInspect");
+#+END_SRC
+
+
+Result:
+    - Generated DOT file: [[file:JavaInspect.dot][JavaInspect.dot]]
+    - Generated PNG image: [[file:JavaInspect.png][JavaInspect.png]]
+
+*** Example 2: GraphViz embedded in another project
+:PROPERTIES:
+:CUSTOM_ID: example-2-graphviz-embedded-in-another-project
+:END:
+1. Download project Sixth [[https://www2.svjatoslav.eu/gitweb/?p=sixth.git;a=snapshot;h=HEAD;sf=tgz][code snapshot]].
+2. Inspect and run *DataGraph.java*.
+
+*** Embedding JavaInspect in your Maven project
+:PROPERTIES:
+:CUSTOM_ID: embedding-javainspect-in-your-maven-project
+:END:
+
+Declare JavaInspect as dependency:
+#+BEGIN_SRC xml
+<dependencies>
+    ...
+    <dependency>
+        <groupId>eu.svjatoslav</groupId>
+        <artifactId>javainspect</artifactId>
+        <version>1.7</version>
+    </dependency>
+    ...
+</dependencies>
+#+END_SRC
+
+
+Add Maven repository to retrieve artifact from:
+#+BEGIN_SRC xml
+<repositories>
+    ...
+    <repository>
+        <id>svjatoslav.eu</id>
+        <name>Svjatoslav repository</name>
+        <url>https://www3.svjatoslav.eu/maven/</url>
+    </repository>
+    ...
+</repositories>
+#+END_SRC
+
+* Source code
+:PROPERTIES:
+:ID:       c47ff9a6-d737-4b73-9393-1c63d2ca1101
+:CUSTOM_ID: getting-the-source-code
+:END:
+
+*This program is free software: released under Creative Commons Zero
+(CC0) license*
+
+Program author:
+- Svjatoslav Agejenko
+- Homepage: https://svjatoslav.eu
+- Email: mailto://svjatoslav@svjatoslav.eu
+- See also: [[https://www.svjatoslav.eu/projects/][Other software projects hosted at svjatoslav.eu]]
+
+*Getting the source code:*
+- [[https://www2.svjatoslav.eu/gitweb/?p=javainspect.git;a=snapshot;h=HEAD;sf=tgz][Download latest source code snapshot in TAR GZ format]]
+- [[https://www2.svjatoslav.eu/gitweb/?p=javainspect.git;a=summary][Browse Git repository online]]
+- You can clone Git repository using git:
+  : git clone https://www3.svjatoslav.eu/git/javainspect.git
diff --git a/doc/legend.png b/doc/legend.png
new file mode 100644 (file)
index 0000000..e206b4f
Binary files /dev/null and b/doc/legend.png differ
diff --git a/doc/usage cli.org b/doc/usage cli.org
new file mode 100644 (file)
index 0000000..d77bdef
--- /dev/null
@@ -0,0 +1,111 @@
+:PROPERTIES:
+:ID:       acf1896a-74b4-4914-acf6-a77075e07f25
+:END:
+
+#+TITLE: JavaInspect - usage from commandline
+#+LANGUAGE: en
+#+LATEX_HEADER: \usepackage[margin=1.0in]{geometry}
+#+LATEX_HEADER: \usepackage{parskip}
+#+LATEX_HEADER: \usepackage[none]{hyphenat}
+
+#+OPTIONS: H:20 num:20
+#+OPTIONS: author:nil
+
+
+* Available commandline arguments
+:PROPERTIES:
+:CUSTOM_ID: available-commandline-arguments
+:END:
+
+
+- -j (existing files)... :: JAR file(s) to render.
+
+- -c (existing directories)... :: Classpath directories
+
+- -n (string) :: Graph name. (default: "graph")
+
+- --debug :: Show debug info.
+
+- -h, --help :: Show commandline usage help.
+
+- -k :: Keep dot file.
+
+- -ho ::
+    Hide orphaned classes.
+
+- -w (one to many strings)... :: Whitelist glob(s).
+
+- -b (one to many strings)... :: Blacklist glob(s).
+
+- -r (one to many strings)... :: root class(es).
+
+- -d (existing directory) :: Target directory. Default is current directory.
+
+- -t (options: png, svg) :: Target image type. Default is: svg.
+
+* Specifying classes to render
+:PROPERTIES:
+:CUSTOM_ID: specifying-classes-to-render
+:END:
+
+Normal Java application has immense complexity. In addition to code
+that was directly written by particular project developers, lots of
+functionality is typically added as frameworks or libraries to the
+project. In addition there is significant Java standard library.
+
+Because JavaInspect uses reflection, it does not easily distinguish
+between those. In normal situation you would rather want to visualize
+only code that was developed specifically for your project and leave
+frameworks like Spring etc. out. If you visualize all classes that are
+possibly reachable from you project, you will easily get huge and
+incomprehensible graph.
+
+JavaInspect can digest compiled Java classes in 2 modes:
+1. Provide list of Jar files. Use *-j* option.
+2. Provide list of filesystem directories that can be used as
+   classpath root. Use *-c* option.
+
+Currently JavaInspect uses following algorithm to add classes to
+rendered graph:
+
+- All classes that were found in Jar files are added to graph by default.
+- None of the classes that were found in filesystem directories are
+  added to the graph by default (unless explicitly referenced). (TODO:
+  for consistency it would be better to add them too by default)
+- If whitelist is specified (*-w* option) everything that is not
+  matched by whitelist pattern(s) will be removed from the graph.
+- If blacklist is specified (*-b* option) everything that is matched
+  by blacklist pattern(s) will be removed from the graph.
+- Root classes can be specified using *-r* option. Root classes will
+  be added to the graph. JavaInspect will then try to recursively
+  discover all classes that were referenced by root class and add
+  those also to the graph.
+
+* Examples
+:PROPERTIES:
+:CUSTOM_ID: examples
+:END:
+
+Visualize java Jar file. All classes. Hide orphaned classes:
+
+#+begin_src sh
+  javainspect \
+      -j target/sixth-3d-*-SNAPSHOT.jar \
+      -d doc/graphs/ \
+      -n "all classes" \
+      -t png -ho
+#+end_src
+
+
+Visualize java Jar file. All classes. Hide orphaned classes. Apply
+whitelist:
+
+#+begin_src sh
+  javainspect \
+      -j target/sixth-3d-*-SNAPSHOT.jar \
+      -d doc/graphs/ \
+      -n "GUI" \
+      -t png \
+      -w "eu.svjatoslav.sixth.e3d.gui.*" \
+      -ho
+#+end_src
diff --git a/pom.xml b/pom.xml
new file mode 100644 (file)
index 0000000..e27daad
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,160 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>eu.svjatoslav</groupId>
+    <artifactId>javainspect</artifactId>
+    <version>1.8-SNAPSHOT</version>
+    <packaging>jar</packaging>
+    <name>JavaInspect</name>
+    <description>Utility to visualize Java code</description>
+
+    <properties>
+        <java.version>1.8</java.version>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+    </properties>
+
+    <organization>
+        <name>svjatoslav.eu</name>
+        <url>http://svjatoslav.eu</url>
+    </organization>
+
+    <build>
+        <finalName>javainspect</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.8.1</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                    <optimize>true</optimize>
+                    <encoding>UTF-8</encoding>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-source-plugin</artifactId>
+                <version>2.2.1</version>
+                <executions>
+                    <execution>
+                        <id>attach-sources</id>
+                        <goals>
+                            <goal>jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <version>2.10.4</version>
+                <executions>
+                    <execution>
+                        <id>attach-javadocs</id>
+                        <goals>
+                            <goal>jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <!-- workaround for https://bugs.openjdk.java.net/browse/JDK-8212233 -->
+                    <javaApiLinks>
+                        <property>
+                            <name>foo</name>
+                            <value>bar</value>
+                        </property>
+                    </javaApiLinks>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-release-plugin</artifactId>
+                <version>2.5.2</version>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.apache.maven.scm</groupId>
+                        <artifactId>maven-scm-provider-gitexe</artifactId>
+                        <version>1.9.4</version>
+                    </dependency>
+                </dependencies>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <version>2.4.3</version>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <configuration>
+                            <transformers>
+                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+                                    <mainClass>eu.svjatoslav.inspector.java.commandline.Main</mainClass>
+                                </transformer>
+                            </transformers>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+
+        <extensions>
+            <extension>
+                <groupId>org.apache.maven.wagon</groupId>
+                <artifactId>wagon-ssh-external</artifactId>
+                <version>2.6</version>
+            </extension>
+        </extensions>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>eu.svjatoslav</groupId>
+            <artifactId>svjatoslavcommons</artifactId>
+            <version>1.9</version>
+        </dependency>
+        <dependency>
+            <groupId>eu.svjatoslav</groupId>
+            <artifactId>cli-helper</artifactId>
+            <version>1.0</version>
+        </dependency>
+    </dependencies>
+
+    <distributionManagement>
+        <snapshotRepository>
+            <id>svjatoslav.eu</id>
+            <name>svjatoslav.eu</name>
+            <url>scpexe://svjatoslav.eu:10006/srv/maven</url>
+        </snapshotRepository>
+        <repository>
+            <id>svjatoslav.eu</id>
+            <name>svjatoslav.eu</name>
+            <url>scpexe://svjatoslav.eu:10006/srv/maven</url>
+        </repository>
+    </distributionManagement>
+
+    <repositories>
+        <repository>
+            <id>svjatoslav.eu</id>
+            <name>Svjatoslav repository</name>
+            <url>https://www3.svjatoslav.eu/maven/</url>
+        </repository>
+    </repositories>
+
+
+    <scm>
+        <connection>scm:git:ssh://n0@svjatoslav.eu:10006/home/n0/git/javainspect.git</connection>
+        <developerConnection>scm:git:ssh://n0@svjatoslav.eu:10006/home/n0/git/javainspect.git</developerConnection>
+      <tag>HEAD</tag>
+  </scm>
+
+</project>
diff --git a/src/main/java/eu/svjatoslav/inspector/java/RenderJavaInspect.java b/src/main/java/eu/svjatoslav/inspector/java/RenderJavaInspect.java
new file mode 100755 (executable)
index 0000000..4d4f39c
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * JavaInspect - Utility to visualize java software.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+
+package eu.svjatoslav.inspector.java;
+
+import eu.svjatoslav.inspector.java.structure.ClassGraph;
+import eu.svjatoslav.inspector.java.structure.Utils;
+
+/**
+ * Demo application that generates a self-visualization of JavaInspect.
+ *
+ * <p>This class demonstrates how to use JavaInspect to analyze and visualize
+ * its own codebase, producing a class diagram showing the internal structure
+ * of the tool.</p>
+ *
+ * @see ClassGraph
+ */
+public class RenderJavaInspect {
+
+    /**
+     * Demonstrates generating a class graph from hand-picked classes.
+     *
+     * <p>This method creates a graph visualization of JavaInspect itself,
+     * including the {@link ClassGraph} and {@link Utils} classes. The output
+     * files are generated in the project root directory:</p>
+     *
+     * <ul>
+     *   <li>JavaInspect.svg - Scalable vector graphics visualization</li>
+     *   <li>JavaInspect.dot - GraphViz DOT source file (kept for reference)</li>
+     * </ul>
+     */
+    private static void handpickClassesExample() {
+        /*
+         * This example demonstrates generating of class graph from hand picked
+         * classes and visualizing GraphViz itself.
+         */
+
+        // Create graph
+        final ClassGraph graph = new ClassGraph();
+
+        // Add some random object to the graph. GraphViz will detect Class from
+        // the object.
+        graph.add(graph);
+
+        // Also add some random class to the graph.
+        graph.add(Utils.class);
+
+        // Keep intermediary GraphViz DOT file for reference.
+        graph.setKeepDotFile(true);
+
+        // Produce SVG image titled "JavaInspect.svg" to the user Desktop
+        // directory
+        graph.generateGraph("JavaInspect");
+    }
+
+    /**
+     * Entry point for the demo application.
+     *
+     * @param args command-line arguments (ignored)
+     */
+    public static void main(final String[] args) {
+
+        handpickClassesExample();
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/eu/svjatoslav/inspector/java/commandline/CommandlineConfiguration.java b/src/main/java/eu/svjatoslav/inspector/java/commandline/CommandlineConfiguration.java
new file mode 100644 (file)
index 0000000..c22f5a7
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * JavaInspect - Utility to visualize java software.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+
+package eu.svjatoslav.inspector.java.commandline;
+
+
+import eu.svjatoslav.commons.cli_helper.parameter_parser.Parser;
+import eu.svjatoslav.commons.cli_helper.parameter_parser.parameter.*;
+
+/**
+ * Configuration holder for JavaInspect command-line arguments.
+ *
+ * <p>This class parses and stores all command-line options for the
+ * JavaInspect tool, providing easy access to JAR files, graph settings,
+ * and filter patterns.</p>
+ *
+ * <p>Supported options:</p>
+ * <ul>
+ *   <li>-j, --jar: JAR files to analyze (required unless -r is specified)</li>
+ *   <li>-c, --classpath: Additional classpath directories</li>
+ *   <li>-n, --name: Graph name for output files</li>
+ *   <li>-t, --type: Output image type (svg or png)</li>
+ *   <li>-d, --directory: Target output directory</li>
+ *   <li>-k, --keep-dot: Keep intermediate DOT file</li>
+ *   <li>-w, --whitelist: Whitelist glob patterns</li>
+ *   <li>-b, --blacklist: Blacklist glob patterns</li>
+ *   <li>-r, --root: Root classes to analyze (instead of all JAR classes)</li>
+ *   <li>-ho, --hide-orphaned: Hide classes with no connections</li>
+ *   <li>--debug: Show debug output</li>
+ *   <li>-h, --help: Show usage help</li>
+ * </ul>
+ *
+ * @see Main
+ * @see Parser
+ */
+public class CommandlineConfiguration {
+
+    /**
+     * JAR files containing classes to analyze.
+     */
+    public FileParameters jarFiles;
+
+    /**
+     * Name for the generated graph files.
+     */
+    public StringParameter graphName;
+
+    /**
+     * Flag to enable debug output during processing.
+     */
+    private NullParameter showDebug;
+
+    /**
+     * Glob patterns for classes to include in the graph.
+     */
+    public StringParameters whitelistGlob;
+
+    /**
+     * Glob patterns for classes to exclude from the graph.
+     */
+    public StringParameters blacklistGlob;
+
+    /**
+     * Specific root classes to analyze (instead of all JAR classes).
+     */
+    public StringParameters rootClasses;
+
+    /**
+     * Flag to show command-line usage help.
+     */
+    public NullParameter showHelp;
+
+    /**
+     * Output image type (SVG or PNG).
+     */
+    public TargetImageTypeParameter targetImageType;
+
+    /**
+     * Flag to keep the intermediate DOT file.
+     */
+    public NullParameter keepDotFile;
+
+    /**
+     * Directory for output files.
+     */
+    public DirectoryParameter targetDirectory;
+
+    /**
+     * Additional classpath directories for resolving dependencies.
+     */
+    public DirectoryParameters classPaths;
+
+    /**
+     * Whether the command-line arguments were parsed successfully.
+     */
+    public final boolean configurationOk;
+
+    /**
+     * Flag to hide orphaned classes (classes with no connections).
+     */
+    public NullParameter hideOrphanedClasses;
+
+    /**
+     * The argument parser instance.
+     */
+    Parser parser;
+
+    /**
+     * Default graph name if not specified on command line.
+     */
+    public static final String DEFAULT_GRAPH_NAME = "graph";
+
+    /**
+     * Parses command-line arguments and initializes all configuration options.
+     *
+     * @param args the command-line arguments to parse
+     */
+    public CommandlineConfiguration(String[] args) {
+        parser = buildCommandlineParameterParser();
+        configurationOk = parser.parse(args);
+    }
+
+    /**
+     * Checks if debug mode is enabled.
+     *
+     * @return true if --debug was specified
+     */
+    public boolean isDebug() {
+        return showDebug.isSpecified();
+    }
+
+    /**
+     * Builds the command-line argument parser with all supported options.
+     *
+     * <p>This method creates a Parser and registers all parameters with
+     * their descriptions, default values, and aliases.</p>
+     *
+     * @return the configured Parser instance
+     */
+    public Parser buildCommandlineParameterParser() {
+        Parser parser = new Parser();
+
+        jarFiles = parser.add(
+                new FileParameters("JAR file(s) to render."))
+                .mustExist()
+                .addAliases("-j");
+
+        classPaths = parser.add(
+                        new DirectoryParameters("Classpath directories"))
+                .mustExist()
+                .addAliases("-c");
+
+        graphName = parser.add(
+                new StringParameter("Graph name. (default: \"" + DEFAULT_GRAPH_NAME + "\")", DEFAULT_GRAPH_NAME))
+                .addAliases("-n");
+
+        showDebug = parser.add(
+                new NullParameter("Show debug info."))
+                .addAliases("--debug");
+
+        showHelp = parser.add(new NullParameter("Show commandline usage help."))
+                .addAliases("-h", "--help");
+
+        keepDotFile = parser.add(
+                new NullParameter("Keep dot file."))
+                .addAliases("-k");
+
+        hideOrphanedClasses = parser.add(
+                new NullParameter("Hide orphaned classes."))
+                .addAliases("-ho");
+
+        whitelistGlob = parser.add(
+                new StringParameters("Whitelist glob(s)."))
+                .addAliases("-w");
+
+        blacklistGlob = parser.add(
+                new StringParameters("Blacklist glob(s)."))
+                .addAliases("-b");
+
+        rootClasses = parser.add(
+                        new StringParameters("root class(es)."))
+                .addAliases("-r");
+
+
+        targetDirectory = parser.add(new DirectoryParameter("Target directory. " +
+                "Default is current directory.").mustExist())
+                .addAliases("-d");
+
+        targetImageType = parser.add(new TargetImageTypeParameter()).addAliases("-t");
+
+        return parser;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/eu/svjatoslav/inspector/java/commandline/Main.java b/src/main/java/eu/svjatoslav/inspector/java/commandline/Main.java
new file mode 100644 (file)
index 0000000..ace1324
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * JavaInspect - Utility to visualize java software.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+
+package eu.svjatoslav.inspector.java.commandline;
+
+import eu.svjatoslav.inspector.java.structure.ClassGraph;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+
+/**
+ * Command-line interface entry point for JavaInspect.
+ *
+ * <p>This class provides the main method for running JavaInspect from the
+ * command line, parsing arguments and generating class visualizations.</p>
+ *
+ * <p>Usage:</p>
+ * <pre>
+ * java -jar javainspect.jar -j myapp.jar -n MyAppGraph
+ * </pre>
+ *
+ * <p>Common options:</p>
+ * <ul>
+ *   <li>-j: JAR files to analyze</li>
+ *   <li>-n: Graph name for output files</li>
+ *   <li>-t: Output format (svg or png)</li>
+ *   <li>-d: Target directory for output</li>
+ *   <li>-w/-b: Whitelist/blacklist glob patterns</li>
+ * </ul>
+ *
+ * @see CommandlineConfiguration
+ * @see ClassGraph
+ */
+public class Main {
+
+    /**
+     * Main entry point for the command-line interface.
+     *
+     * <p>Parses command-line arguments, builds the class graph from JAR files,
+     * and generates the visualization.</p>
+     *
+     * @param args command-line arguments
+     * @throws IOException if JAR files cannot be read
+     * @throws ClassNotFoundException if classes cannot be loaded
+     */
+    public static void main(String[] args) throws IOException, ClassNotFoundException {
+        CommandlineConfiguration configuration = new CommandlineConfiguration(args);
+        if (!configuration.configurationOk) {
+            configuration.parser.showHelp();
+            System.exit(1);
+        }
+
+        if (configuration.showHelp.isSpecified()) configuration.parser.showHelp();
+
+        getClassGraph(configuration).generateGraph(configuration.graphName.getValue());
+
+        if (configuration.isDebug())
+            System.out.println("Graph ready.");
+    }
+
+    /**
+     * Builds and configures the ClassGraph from command-line options.
+     *
+     * <p>This method:</p>
+     * <ol>
+     *   <li>Creates a URLClassLoader from the JAR files and classpath</li>
+     *   <li>Creates a ClassGraph and configures output options</li>
+     *   <li>Loads either specified root classes or all classes from JARs</li>
+     *   <li>Applies whitelist/blacklist filters</li>
+     *   <li>Hides orphaned classes if requested</li>
+     * </ol>
+     *
+     * @param configuration the parsed command-line configuration
+     * @return the configured ClassGraph ready for generation
+     * @throws IOException if JAR files cannot be read
+     * @throws ClassNotFoundException if classes cannot be loaded
+     */
+    private static ClassGraph getClassGraph(CommandlineConfiguration configuration) throws IOException, ClassNotFoundException {
+        List<File> jarFiles = configuration.jarFiles.getValue();
+
+        URLClassLoader classLoader = new URLClassLoader(
+                getClasspathUrls(jarFiles, configuration.classPaths.getValue()),
+                configuration.getClass().getClassLoader());
+
+        ClassGraph classGraph = new ClassGraph();
+
+        if (configuration.targetDirectory.isSpecified())
+            classGraph.setTargetDirectory(configuration.targetDirectory.getValue());
+
+        if (configuration.targetImageType.isSpecified())
+            classGraph.setTargetImageType(configuration.targetImageType.getValue());
+
+        classGraph.setKeepDotFile(configuration.keepDotFile.getValue());
+
+        if (configuration.rootClasses.isSpecified()) {
+            // add only selected root classes
+            for (String rootClass : configuration.rootClasses.getValue())
+                attemptClassAdditionByName(classLoader, classGraph, configuration, rootClass);
+
+        } else {
+            // add all classes in the jar files to graph
+            for (File jarFile : jarFiles)
+                addJarToGraph(jarFile, classLoader, classGraph, configuration);
+        }
+        configuration.blacklistGlob.getValue().forEach(classGraph::blacklistClassGlob);
+        configuration.whitelistGlob.getValue().forEach(classGraph::whitelistClassGlob);
+
+        if (configuration.hideOrphanedClasses.getValue())
+            classGraph.hideOrphanedClasses();
+
+        return classGraph;
+    }
+
+    /**
+     * Builds an array of URLs for the classpath from JAR files and directories.
+     *
+     * @param jarFiles list of JAR files to include
+     * @param classpathDirectories list of classpath directories to include
+     * @return array of URLs for the class loader
+     */
+    private static URL[] getClasspathUrls(List<File> jarFiles, List<File> classpathDirectories) {
+        List<URL> urls = new ArrayList<>();
+
+        classpathDirectories.forEach(classpathDirectory -> {
+            try {
+                urls.add(classpathDirectory.toURI().toURL());
+            } catch (MalformedURLException e) {
+                throw new RuntimeException(e);
+            }
+        });
+
+        jarFiles.forEach((File file) -> {
+            try {
+                urls.add(file.toURI().toURL());
+            } catch (MalformedURLException e) {
+                throw new RuntimeException(e);
+            }
+        });
+
+        return urls.toArray(new URL[urls.size()]);
+    }
+
+    /**
+     * Adds all classes from a JAR file to the class graph.
+     *
+     * <p>Iterates through all .class entries in the JAR and attempts to
+     * load each one into the graph.</p>
+     *
+     * @param jarFile the JAR file to process
+     * @param classLoader the URLClassLoader for loading classes
+     * @param classGraph the graph to add classes to
+     * @param configuration the command-line configuration for debug output
+     * @throws IOException if the JAR cannot be read
+     * @throws ClassNotFoundException if a class cannot be found
+     */
+    private static void addJarToGraph(
+            File jarFile, URLClassLoader classLoader, ClassGraph classGraph, CommandlineConfiguration configuration)
+            throws IOException, ClassNotFoundException {
+
+        for (String className : getClassNamesFromJar(jarFile))
+            attemptClassAdditionByName(classLoader, classGraph, configuration, className);
+    }
+
+    /**
+     * Attempts to load and add a class to the graph by its name.
+     *
+     * <p>This method gracefully handles NoClassDefFoundError, which can occur
+     * when a class references other classes not present in the JAR.</p>
+     *
+     * @param classLoader the URLClassLoader for loading classes
+     * @param classGraph the graph to add the class to
+     * @param configuration the command-line configuration for debug output
+     * @param className the fully qualified class name
+     * @throws ClassNotFoundException if the class cannot be found
+     */
+    private static void attemptClassAdditionByName(URLClassLoader classLoader, ClassGraph classGraph, CommandlineConfiguration configuration, String className) throws ClassNotFoundException {
+        if (configuration.isDebug())
+            System.out.println("Adding class to graph: " + className);
+        try {
+            classGraph.add(loadClassByName(classLoader, className));
+        } catch (NoClassDefFoundError e) {
+            if (configuration.isDebug())
+                System.out.println("Class definition was not found.");
+            // Sometimes referenced classes are not found in the same Jar.
+            // Let's ignore this and proceed with the classes that we have.
+        }
+    }
+
+    /**
+     * Loads a class by its fully qualified name using the class loader.
+     *
+     * @param classLoader the URLClassLoader for loading classes
+     * @param className the fully qualified class name
+     * @return the loaded Class object
+     * @throws ClassNotFoundException if the class cannot be found
+     */
+    private static Class loadClassByName(URLClassLoader classLoader, String className) throws ClassNotFoundException {
+        return Class.forName(className, true, classLoader);
+    }
+
+    /**
+     * Extracts all class names from a JAR file.
+     *
+     * <p>Scans the JAR for .class entries and converts their paths to
+     * fully qualified class names.</p>
+     *
+     * @param jarFile the JAR file to scan
+     * @return list of fully qualified class names
+     * @throws IOException if the JAR cannot be read
+     */
+    public static List<String> getClassNamesFromJar(File jarFile) throws IOException {
+        List<String> result = new ArrayList<>();
+        try (
+                JarInputStream jarInputStream = new JarInputStream(new FileInputStream(jarFile))
+        ) {
+            while (true) {
+                JarEntry jarEntry = jarInputStream.getNextJarEntry();
+                if (jarEntry == null)
+                    break;
+
+                if (isClassFile(jarEntry))
+                    result.add(getClassNameFromFileName(jarEntry));
+            }
+
+            return result;
+        }
+    }
+
+    /**
+     * Checks if a JAR entry is a Java class file.
+     *
+     * @param jarEntry the JAR entry to check
+     * @return true if the entry name ends with .class
+     */
+    private static boolean isClassFile(JarEntry jarEntry) {
+        return jarEntry.getName().endsWith(".class");
+    }
+
+    /**
+     * Converts a JAR entry path to a fully qualified class name.
+     *
+     * <p>Converts path separators (/) to package separators (.) and
+     * removes the .class extension.</p>
+     *
+     * @param jarEntry the JAR entry containing the class file path
+     * @return the fully qualified class name
+     */
+    private static String getClassNameFromFileName(JarEntry jarEntry) {
+        String result = jarEntry.getName().replaceAll("/", "\\.");
+        return result.substring(0, result.lastIndexOf('.'));
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/eu/svjatoslav/inspector/java/commandline/TargetImageTypeParameter.java b/src/main/java/eu/svjatoslav/inspector/java/commandline/TargetImageTypeParameter.java
new file mode 100644 (file)
index 0000000..917be06
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * JavaInspect - Utility to visualize java software.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+
+package eu.svjatoslav.inspector.java.commandline;
+
+import eu.svjatoslav.commons.cli_helper.parameter_parser.Parameter;
+import eu.svjatoslav.commons.string.String2;
+import eu.svjatoslav.inspector.java.structure.TargetImageType;
+
+import java.util.Arrays;
+
+import static eu.svjatoslav.commons.cli_helper.parameter_parser.ArgumentCount.SINGLE;
+
+/**
+ * Command-line parameter type for specifying output image format.
+ *
+ * <p>This parameter accepts "svg" or "png" values and converts them
+ * to the corresponding {@link TargetImageType} enum values.</p>
+ *
+ * @see TargetImageType
+ * @see CommandlineConfiguration
+ */
+public class TargetImageTypeParameter extends Parameter<TargetImageType, TargetImageTypeParameter> {
+
+    /**
+     * Creates a target image type parameter with default SVG format.
+     *
+     * <p>The parameter accepts a single argument value: either "svg" or "png".</p>
+     */
+    public TargetImageTypeParameter(){
+        super("Target image type. Default is: svg.", SINGLE);
+    }
+
+    /**
+     * Describes the valid format options for this parameter.
+     *
+     * <p>Returns a comma-separated list of available image types.</p>
+     *
+     * @return description string like "options: svg, png"
+     */
+    @Override
+    public String describeFormat() {
+        String2 result = new String2("");
+
+        Arrays.stream(TargetImageType.values())
+                .forEach(targetImageType -> result.appendWithSeparator(", ", targetImageType.fileExtension));
+
+        result.prepend("options: ");
+
+        return result.toString();
+    }
+
+    /**
+     * Returns the parsed TargetImageType value from the command-line argument.
+     *
+     * <p>Converts the argument to uppercase and looks up the corresponding
+     * enum constant.</p>
+     *
+     * @return the TargetImageType matching the argument value
+     */
+    @Override
+    public TargetImageType getValue() {
+        return TargetImageType.valueOf(arguments.get(0).toUpperCase());
+    }
+
+    /**
+     * Validates that a value is a valid image type name.
+     *
+     * @param value the value to validate
+     * @return true if the value is "svg" or "png", false otherwise
+     */
+    @Override
+    public boolean validate(String value) {
+        try {
+            TargetImageType.valueOf(value.toUpperCase());
+        } catch (final IllegalArgumentException exception) {
+            return false;
+        }
+
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/eu/svjatoslav/inspector/java/commandline/package-info.java b/src/main/java/eu/svjatoslav/inspector/java/commandline/package-info.java
new file mode 100644 (file)
index 0000000..a3a051c
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * JavaInspect - Utility to visualize java software.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+
+/**
+ * Provides the command-line interface for JavaInspect.
+ *
+ * <p>This package contains classes for parsing command-line arguments
+ * and launching the class graph generation process.</p>
+ *
+ * @see eu.svjatoslav.inspector.java.commandline.Main
+ * @see eu.svjatoslav.inspector.java.commandline.CommandlineConfiguration
+ */
+package eu.svjatoslav.inspector.java.commandline;
\ No newline at end of file
diff --git a/src/main/java/eu/svjatoslav/inspector/java/structure/ClassDescriptor.java b/src/main/java/eu/svjatoslav/inspector/java/structure/ClassDescriptor.java
new file mode 100755 (executable)
index 0000000..7e40e89
--- /dev/null
@@ -0,0 +1,836 @@
+/*
+ * JavaInspect - Utility to visualize java software.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+
+package eu.svjatoslav.inspector.java.structure;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.*;
+
+/**
+ * Represents a single Java class for visualization in the class graph.
+ *
+ * <p>Class descriptors are the primary nodes in the visualization, capturing
+ * class metadata, fields, methods, inheritance relationships, and interface
+ * implementations. Each descriptor generates GraphViz DOT format output
+ * for rendering as an HTML-like table node with connection edges.</p>
+ *
+ * <p>Classes are analyzed using Java reflection to extract:</p>
+ * <ul>
+ *   <li>Superclass relationships</li>
+ *   <li>Implemented interfaces</li>
+ *   <li>Declared fields and their types</li>
+ *   <li>Methods and their return types</li>
+ *   <li>Generic type information</li>
+ * </ul>
+ *
+ * @see ClassGraph
+ * @see FieldDescriptor
+ * @see MethodDescriptor
+ * @see GraphElement
+ */
+public class ClassDescriptor implements GraphElement, Comparable<ClassDescriptor> {
+
+    /**
+     * Maximum number of incoming references to show for a class.
+     *
+     * <p>Classes with more than this number of references are drawn with
+     * thicker borders to indicate high coupling.</p>
+     */
+    private static final int MAX_REFERECNES_COUNT = 10;
+
+    /**
+     * List of interfaces implemented by this class.
+     */
+    final List<ClassDescriptor> interfaces = new ArrayList<>();
+
+    /**
+     * Map of field names to their descriptors.
+     */
+    private final Map<String, FieldDescriptor> nameToFieldMap = new TreeMap<>();
+
+    /**
+     * Sorted set of method descriptors.
+     */
+    private final SortedSet<MethodDescriptor> methods = new TreeSet<>();
+
+    /**
+     * The class graph that owns this descriptor.
+     */
+    private final ClassGraph classGraph;
+
+    /**
+     * Whether this class is an enum.
+     */
+    boolean isEnum;
+
+    /**
+     * Whether this class is an interface.
+     */
+    boolean isInterface;
+
+    /**
+     * Whether this class represents an array type.
+     */
+    boolean isArray;
+
+    /**
+     * The superclass descriptor, or null for Object or primitives.
+     */
+    ClassDescriptor superClass;
+
+    /**
+     * The fully qualified class name (e.g., "java.lang.String").
+     */
+    private String fullyQualifiedName;
+
+    /**
+     * Color for incoming reference arrows to this class.
+     *
+     * <p>Assigned from the dark color palette to distinguish this class's
+     * incoming edges from others.</p>
+     */
+    private String distinctiveReferenceColor;
+
+    /**
+     * Color for interface implementation arrows.
+     */
+    private String interfaceColor;
+
+    /**
+     * Color for superclass inheritance arrows.
+     */
+    private String superClassColor;
+
+    /**
+     * Whether this class should be rendered in the graph.
+     *
+     * <p>Classes can be hidden if they are system types, filtered by glob
+     * patterns, or have no interesting references.</p>
+     */
+    private boolean isShown = true;
+
+    /**
+     * Count of field and method references pointing to this class.
+     *
+     * <p>Used to determine if the class should show its reference arrows.</p>
+     */
+    private int referencesCount = 0;
+
+    /**
+     * Count of implementations for interface classes.
+     *
+     * <p>Only relevant for interfaces; counts how many classes implement this interface.</p>
+     */
+    private int implementationsCount = 0;
+
+    /**
+     * Count of extensions for this class.
+     *
+     * <p>Counts how many classes extend this class as their superclass.</p>
+     */
+    private int extensionsCount = 0;
+
+    /**
+     * The component type for array classes.
+     *
+     * <p>For example, for String[] this would be the String descriptor.</p>
+     */
+    private ClassDescriptor arrayComponent;
+
+    /**
+     * Creates a class descriptor associated with a class graph.
+     *
+     * @param classGraph the graph that will contain this descriptor
+     */
+    public ClassDescriptor(final ClassGraph classGraph) {
+        this.classGraph = classGraph;
+    }
+
+    /**
+     * Analyzes a Java class using reflection to populate this descriptor.
+     *
+     * <p>This method extracts the class name, type information (enum, interface, array),
+     * declared fields, methods, superclass, and implemented interfaces.</p>
+     *
+     * @param clazz the Java Class to analyze
+     */
+    protected void analyzeClass(final Class<?> clazz) {
+
+        fullyQualifiedName = clazz.getName();
+
+        isArray = clazz.isArray();
+
+        if (isArray) {
+            final Class<?> componentType = clazz.getComponentType();
+            arrayComponent = getClassGraph().getOrCreateClassDescriptor(
+                    componentType);
+        }
+
+        // System.out.println("class: " + fullyQualifiedName);
+
+        isEnum = clazz.isEnum();
+
+        isInterface = clazz.isInterface();
+
+        if (!isVisible())
+            return;
+
+        try {
+            indexFields(clazz.getDeclaredFields());
+            indexFields(clazz.getFields());
+        } catch (NoClassDefFoundError error) {
+            // TODO: better logging of this error
+            System.out.println(error.toString());
+        }
+
+        indexMethods(clazz);
+
+        for (final Class interfaceClass : clazz.getInterfaces()) {
+            final ClassDescriptor interfaceClassDescriptor = getClassGraph()
+                    .getOrCreateClassDescriptor(interfaceClass);
+            interfaceClassDescriptor.registerImplementation();
+            interfaces.add(interfaceClassDescriptor);
+        }
+
+        superClass = getClassGraph().getOrCreateClassDescriptor(
+                clazz.getSuperclass());
+        if (superClass != null)
+            superClass.registerExtension();
+
+    }
+
+    /**
+     * Determines whether incoming reference arrows should be shown for this class.
+     *
+     * <p>Classes with too many references are visually cluttered if all arrows
+     * are shown, so this method returns false above the threshold.</p>
+     *
+     * @return true if reference count is within the display threshold
+     */
+    protected boolean areReferencesShown() {
+        return referencesCount <= MAX_REFERECNES_COUNT;
+    }
+
+    /**
+     * Appends DOT format edges for field references to other classes.
+     *
+     * @param result the StringBuffer to append to
+     */
+    private void enlistFieldReferences(final StringBuffer result) {
+        if (nameToFieldMap.isEmpty())
+            return;
+
+        result.append("\n");
+        result.append("    // field references to other classes\n");
+        nameToFieldMap.forEach((fieldName, field) -> result.append(field.getDot()));
+    }
+
+    /**
+     * Appends DOT format HTML rows for fields within the class node.
+     *
+     * @param result the StringBuffer to append to
+     */
+    private void enlistFields(final StringBuffer result) {
+        if (nameToFieldMap.isEmpty())
+            return;
+
+        result.append("\n");
+        result.append("    // fields:\n");
+
+        // enlist fields
+        for (final Map.Entry<String, FieldDescriptor> entry : nameToFieldMap
+                .entrySet())
+            result.append(entry.getValue().getEmbeddedDot());
+    }
+
+    /**
+     * Appends DOT format edges for implemented interface relationships.
+     *
+     * <p>Interface relationships are shown as dotted forward arrows.</p>
+     *
+     * @param result the StringBuffer to append to
+     */
+    private void enlistImplementedInterfaces(final StringBuffer result) {
+        if (interfaces.isEmpty())
+            return;
+
+        result.append("\n");
+        result.append("    // interfaces implemented by class: "
+                + fullyQualifiedName + "\n");
+
+        for (final ClassDescriptor interfaceDescriptor : interfaces) {
+            if (!interfaceDescriptor.isVisible())
+                continue;
+
+            if (!interfaceDescriptor.areReferencesShown())
+                continue;
+
+            result.append("    " + interfaceDescriptor.getGraphId() + " -> "
+                    + getGraphId() + "[style=\"dotted\", color=\""
+                    + interfaceDescriptor.getInterfaceColor()
+                    + "\", penwidth=10, dir=\"forward\"];\n");
+        }
+    }
+
+    /**
+     * Appends DOT format edges for method return type references.
+     *
+     * @param result the StringBuffer to append to
+     */
+    private void enlistMethodReferences(final StringBuffer result) {
+        if (methods.isEmpty())
+            return;
+
+        result.append("\n");
+        result.append("    // method references to other classes\n");
+        for (final MethodDescriptor methodDescriptor : methods)
+            result.append(methodDescriptor.getDot());
+    }
+
+    /**
+     * Appends DOT format HTML rows for methods within the class node.
+     *
+     * @param result the StringBuffer to append to
+     */
+    private void enlistMethods(final StringBuffer result) {
+        if (methods.isEmpty())
+            return;
+
+        result.append("\n");
+        result.append("    // methods:\n");
+
+        // enlist methods
+        for (final MethodDescriptor methodDescriptor : methods)
+            result.append(methodDescriptor.getEmbeddedDot());
+    }
+
+    /**
+     * Appends DOT format edge for the superclass relationship.
+     *
+     * <p>Superclass arrows point from parent to child with thick styling.</p>
+     *
+     * @param result the StringBuffer to append to
+     */
+    private void enlistSuperClass(final StringBuffer result) {
+        if (superClass == null)
+            return;
+
+        if (!superClass.isVisible())
+            return;
+
+        if (!superClass.areReferencesShown())
+            return;
+
+        result.append("\n");
+        result.append("    // super class for: " + fullyQualifiedName + "\n");
+
+        result.append("    " + superClass.getGraphId() + " -> " + getGraphId()
+                + "[ color=\"" + superClass.getSuperClassColor()
+                + "\", penwidth=10, dir=\"forward\"];\n");
+    }
+
+    /**
+     * Generates the DOT format header for the class node.
+     *
+     * <p>This creates the HTML table structure with package name, parent class
+     * name (for nested classes), and the class name itself.</p>
+     *
+     * @param result the StringBuffer to append to
+     */
+    private void generateDotHeader(final StringBuffer result) {
+        result.append("\n");
+        result.append("// Class: " + fullyQualifiedName + "\n");
+
+        result.append("    " + getGraphId() + "[label=<<TABLE "
+                + getBackgroundColor() + " BORDER=\"" + getBorderWidth()
+                + "\" CELLBORDER=\"1\" CELLSPACING=\"0\">\n");
+
+        result.append("\n");
+        result.append("    // class descriptor header\n");
+        result.append("    <TR><TD colspan=\"2\" PORT=\"f0\">"
+                + "<FONT POINT-SIZE=\"8.0\" >" + getPackageName()
+                + "</FONT><br/>");
+
+        final String parentClassesName = getParentClassesName();
+        if (parentClassesName.length() > 0)
+            result.append("<FONT POINT-SIZE=\"12.0\"><B>" + parentClassesName
+                    + "</B></FONT><br/>\n");
+
+        result.append("<FONT POINT-SIZE=\"25.0\"><B>" + getClassName(false)
+                + "</B></FONT>" + "</TD></TR>\n");
+    }
+
+    /**
+     * Returns the HTML background color attribute for this class.
+     *
+     * <p>Enums have navajowhite2, interfaces have darkslategray1,
+     * regular classes have no background color.</p>
+     *
+     * @return the bgcolor attribute string, or empty string for regular classes
+     */
+    private String getBackgroundColor() {
+        String bgColor = "";
+
+        if (isEnum)
+            bgColor = "bgcolor=\"navajowhite2\"";
+
+        if (isInterface)
+            bgColor = "bgcolor=\"darkslategray1\"";
+
+        return bgColor;
+    }
+
+    /**
+     * Returns the border width for this class node.
+     *
+     * <p>Classes with many references have thicker borders (4) to indicate
+     * high coupling, normal classes have thin borders (1).</p>
+     *
+     * @return "4" for highly referenced classes, "1" otherwise
+     */
+    private String getBorderWidth() {
+
+        if (!areReferencesShown())
+            return "4";
+        return "1";
+    }
+
+    /**
+     * Returns the owning class graph.
+     *
+     * @return the ClassGraph instance that created this descriptor
+     */
+    protected ClassGraph getClassGraph() {
+        return classGraph;
+    }
+
+    /**
+     * Returns the simple class name, optionally with array indicator.
+     *
+     * <p>For nested classes, dollar signs are replaced with dots.
+     * For array classes, the component type name is returned instead.</p>
+     *
+     * @param differentiateArray if true, append "[]" for array types
+     * @return the simple class name
+     */
+    protected String getClassName(final boolean differentiateArray) {
+        // this is needed for nested classes
+        final String actualClassName = fullyQualifiedName.replace('$', '.');
+
+        String result;
+        if (isArray) {
+            // for arrays use array component instead of array class name
+            result = arrayComponent.fullyQualifiedName;
+            if (result.contains(".")) {
+                final int i = result.lastIndexOf('.');
+                result = result.substring(i + 1);
+            }
+        } else {
+            final int i = actualClassName.lastIndexOf('.');
+            result = actualClassName.substring(i + 1);
+        }
+
+        if (differentiateArray)
+            if (isArray)
+                result += " []";
+
+        // this is needed for nested classes
+        // result = result.replace('$', '.');
+        return result;
+    }
+
+    /**
+     * Returns the distinctive color for incoming reference arrows.
+     *
+     * <p>Colors are assigned from the dark palette on first access.</p>
+     *
+     * @return the GraphViz color name for this class's reference edges
+     */
+    protected String getColor() {
+        if (distinctiveReferenceColor == null)
+            distinctiveReferenceColor = Utils.getNextDarkColor();
+
+        return distinctiveReferenceColor;
+    }
+
+    /**
+     * Generates the complete DOT format representation for this class.
+     *
+     * <p>This creates the class node (HTML table) and all outgoing edges
+     * for fields, methods, interfaces, and superclass.</p>
+     *
+     * @return DOT format string for the class, or empty string if not visible
+     */
+    @Override
+    public String getDot() {
+        if (!isVisible())
+            return "";
+
+        if (isArray)
+            return "";
+
+        final StringBuffer result = new StringBuffer();
+
+        generateDotHeader(result);
+
+        enlistFields(result);
+
+        enlistMethods(result);
+
+        result.append("    </TABLE>>, shape=\"none\"];\n");
+
+        enlistFieldReferences(result);
+
+        enlistMethodReferences(result);
+
+        enlistImplementedInterfaces(result);
+
+        enlistSuperClass(result);
+
+        return result.toString();
+    }
+
+    /**
+     * Returns null as classes are not embedded within other nodes.
+     *
+     * <p>This method is not applicable for class-level descriptors.</p>
+     *
+     * @return null
+     */
+    @Override
+    public String getEmbeddedDot() {
+        return null;
+    }
+
+    /**
+     * Returns a field matching the given name, ignoring case.
+     *
+     * <p>Used to check if getter/setter methods correspond to existing fields.</p>
+     *
+     * @param fieldToSearch the field name to search for (case insensitive)
+     * @return the matching FieldDescriptor, or null if not found
+     */
+    protected FieldDescriptor getFieldIgnoreCase(final String fieldToSearch) {
+
+        for (final String fieldName : nameToFieldMap.keySet())
+            if (fieldToSearch.equalsIgnoreCase(fieldName))
+                return nameToFieldMap.get(fieldName);
+
+        return null;
+    }
+
+    /**
+     * Returns the fully qualified class name.
+     *
+     * @return the fully qualified name (e.g., "java.lang.String")
+     */
+    protected String getFullyQualifiedName() {
+        return fullyQualifiedName;
+    }
+
+    /**
+     * Returns the GraphViz identifier for this class.
+     *
+     * <p>The ID is derived from the fully qualified name, with dots and
+     * special characters replaced with underscores.</p>
+     *
+     * @return unique identifier in format "class_fully_qualified_name"
+     */
+    @Override
+    public String getGraphId() {
+
+        return "class_"
+                + fullyQualifiedName
+                .replace('.', '_')
+                .replace(";", "")
+                .replace("[[", "")
+                .replace("[L", "")
+                .replace("[[L", "") // array of arrays
+                .replace("[[[L", "") // array of arrays of arrays
+                .replace('$', '_');
+    }
+
+    /**
+     * Returns the color for interface implementation arrows.
+     *
+     * @return the GraphViz color name from the dark palette
+     */
+    private String getInterfaceColor() {
+        if (interfaceColor == null)
+            interfaceColor = Utils.getNextDarkColor();
+
+        return interfaceColor;
+    }
+
+    /**
+     * Creates or retrieves a field descriptor for a reflection Field.
+     *
+     * <p>If a descriptor for the field already exists, it is returned.
+     * Otherwise, a new descriptor is created, added to the map, and analyzed.</p>
+     *
+     * @param field the Java reflection Field
+     * @return the FieldDescriptor for the field
+     */
+    private FieldDescriptor getOrCreateFieldDescriptor(final Field field) {
+
+        final String fieldName = field.getName();
+
+        if (nameToFieldMap.containsKey(fieldName))
+            return nameToFieldMap.get(fieldName);
+        final FieldDescriptor newFieldDescriptor = new FieldDescriptor(this);
+        nameToFieldMap.put(fieldName, newFieldDescriptor);
+
+        newFieldDescriptor.analyzeField(field);
+
+        return newFieldDescriptor;
+    }
+
+    /**
+     * Counts the total outgoing references from this class.
+     *
+     * <p>Includes method return types, field types, implemented interfaces,
+     * and superclass.</p>
+     *
+     * @return the count of visible outgoing references
+     */
+    private int getOutgoingReferencesCount() {
+        int result = 0;
+
+        // count method references
+        for (final MethodDescriptor methodDescriptor : methods)
+            result += methodDescriptor.getOutsideVisibleReferencesCount();
+
+        // count field references
+        for (final FieldDescriptor fieldDescriptor : nameToFieldMap.values())
+            result += fieldDescriptor.getOutsideVisibleReferencesCount();
+
+        // count implemented interfaces
+        for (final ClassDescriptor classDescriptor : interfaces)
+            if (classDescriptor.isVisible())
+                result++;
+
+        // count superclass
+        if (superClass != null)
+            if (superClass.isVisible())
+                result++;
+
+        return result;
+    }
+
+    /**
+     * Returns the package name portion of the fully qualified name.
+     *
+     * @return the package name, or empty string for default package
+     */
+    private String getPackageName() {
+
+        final int i = fullyQualifiedName.lastIndexOf('.');
+
+        if (i == -1)
+            return "";
+
+        return fullyQualifiedName.substring(0, i).replace("[L", "");
+    }
+
+    /**
+     * Returns the parent class names for nested classes.
+     *
+     * <p>For nested classes like Outer.Inner, this returns "Outer".</p>
+     *
+     * @return the parent class path, or empty string for non-nested classes
+     */
+    private String getParentClassesName() {
+        int i = fullyQualifiedName.lastIndexOf('.');
+        final String fullClassName = fullyQualifiedName.substring(i + 1);
+
+        i = fullClassName.lastIndexOf('$');
+        if (i == -1)
+            return "";
+        final String parentClassesName = fullClassName.substring(0, i);
+        return parentClassesName.replace('$', '.');
+    }
+
+    /**
+     * Returns the color for superclass inheritance arrows.
+     *
+     * @return the GraphViz color name from the light palette
+     */
+    private String getSuperClassColor() {
+        if (superClassColor == null)
+            superClassColor = Utils.getNextLightColor();
+
+        return superClassColor;
+    }
+
+    /**
+     * Checks if this class has a field matching the given name, ignoring case.
+     *
+     * @param fieldToSearch the field name to search for (case insensitive)
+     * @return true if a matching field exists
+     */
+    protected boolean hasFieldIgnoreCase(final String fieldToSearch) {
+
+        for (final String fieldName : nameToFieldMap.keySet())
+            if (fieldToSearch.equalsIgnoreCase(fieldName))
+                return true;
+
+        return false;
+    }
+
+    /**
+     * Marks this class as hidden, excluding it from the graph output.
+     */
+    private void hide() {
+        isShown = false;
+    }
+
+    /**
+     * Hides this class if it has no incoming or outgoing references.
+     *
+     * <p>Orphaned classes with no connections add visual noise without
+     * providing useful information about relationships.</p>
+     */
+    protected void hideClassIfNoReferences() {
+        if (!isVisible())
+            return;
+
+        final int totalReferencesCount = getOutgoingReferencesCount()
+                + referencesCount + extensionsCount + implementationsCount;
+
+        if (totalReferencesCount == 0) {
+            hide();
+            return;
+        }
+
+    }
+
+    /**
+     * Adds all fields from an array to the field map.
+     *
+     * @param fields the array of Java reflection Fields to index
+     */
+    private void indexFields(final Field[] fields) {
+        for (final Field field : fields)
+            getOrCreateFieldDescriptor(field);
+    }
+
+    /**
+     * Analyzes and adds all public methods from a class.
+     *
+     * @param clazz the Java Class to extract methods from
+     */
+    private void indexMethods(final Class<?> clazz) {
+        for (final Method method : clazz.getMethods()) {
+            final MethodDescriptor methodDescriptor = new MethodDescriptor(
+                    this, method.getName());
+
+            methods.add(methodDescriptor);
+
+            methodDescriptor.analyze(method);
+        }
+
+    }
+
+    /**
+     * Determines whether this class should be included in the graph.
+     *
+     * <p>A class is hidden if it is:</p>
+     * <ul>
+     *   <li>A primitive or system data type</li>
+     *   <li>In a system package (java.*, javax.*, sun.*)</li>
+     *   <li>Filtered by blacklist/whitelist glob patterns</li>
+     *   <li>An array of primitives</li>
+     *   <li>Explicitly marked as hidden</li>
+     * </ul>
+     *
+     * @return true if the class should be rendered, false otherwise
+     */
+    @Override
+    public boolean isVisible() {
+
+        if (Utils.isSystemDataType(fullyQualifiedName))
+            return false;
+
+        if (Utils.isSystemPackage(fullyQualifiedName))
+            return false;
+
+        if (!getClassGraph().isClassShown(fullyQualifiedName))
+            return false;
+
+        if (isArray)
+            if (arrayComponent != null)
+                if (Utils.isSystemDataType(arrayComponent.fullyQualifiedName))
+                    // Do not show references to primitive data types in arrays.
+                    // That is: there is no point to show reference to byte when
+                    // we have class with byte array field.
+                    return false;
+
+        return isShown;
+    }
+
+    /**
+     * Registers that another class extends this class.
+     */
+    protected void registerExtension() {
+        extensionsCount++;
+    }
+
+    /**
+     * Registers that another class implements this interface.
+     */
+    protected void registerImplementation() {
+        implementationsCount++;
+    }
+
+    /**
+     * Registers a field or method reference to this class.
+     */
+    protected void registerReference() {
+        referencesCount++;
+    }
+
+    /**
+     * Compares this descriptor to another for equality.
+     *
+     * <p>Equality is based on fully qualified name.</p>
+     *
+     * @param o the object to compare
+     * @return true if the objects represent the same class
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ClassDescriptor)) return false;
+
+        ClassDescriptor that = (ClassDescriptor) o;
+
+        return getFullyQualifiedName().equals(that.getFullyQualifiedName());
+
+    }
+
+    /**
+     * Returns a hash code based on the fully qualified name.
+     *
+     * @return hash code for this descriptor
+     */
+    @Override
+    public int hashCode() {
+        return getFullyQualifiedName().hashCode();
+    }
+
+    /**
+     * Compares this descriptor to another for sorting.
+     *
+     * <p>Classes are sorted by fully qualified name.</p>
+     *
+     * @param o the other descriptor to compare
+     * @return negative, zero, or positive based on name comparison
+     */
+    @Override
+    public int compareTo(ClassDescriptor o) {
+        return fullyQualifiedName.compareTo(o.fullyQualifiedName);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/eu/svjatoslav/inspector/java/structure/ClassGraph.java b/src/main/java/eu/svjatoslav/inspector/java/structure/ClassGraph.java
new file mode 100755 (executable)
index 0000000..8d2a759
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * JavaInspect - Utility to visualize java software.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+
+package eu.svjatoslav.inspector.java.structure;
+
+import eu.svjatoslav.commons.string.GlobMatcher;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static java.io.File.separator;
+import static java.lang.System.getProperty;
+
+/**
+ * Central graph container for collecting and visualizing Java class relationships.
+ *
+ * <p>ClassGraph is the main entry point for JavaInspect. It collects classes
+ * through reflection, builds a graph of relationships, and generates GraphViz
+ * DOT format output that can be rendered to SVG or PNG images.</p>
+ *
+ * <p>Typical usage:</p>
+ * <pre>
+ * ClassGraph graph = new ClassGraph();
+ * graph.add(myObject);
+ * graph.add(MyClass.class);
+ * graph.setKeepDotFile(true);
+ * graph.generateGraph("MyVisualization");
+ * </pre>
+ *
+ * <p>The graph can be configured with:</p>
+ * <ul>
+ *   <li>Target directory for output files</li>
+ *   <li>Image format (SVG or PNG)</li>
+ *   <li>Whitelist/blacklist glob patterns for filtering classes</li>
+ *   <li>Option to hide orphaned classes with no connections</li>
+ * </ul>
+ *
+ * @see ClassDescriptor
+ * @see TargetImageType
+ */
+public class ClassGraph {
+
+    /**
+     * Maps fully qualified class names to their descriptors.
+     *
+     * <p>This is the primary storage for all analyzed classes in the graph.</p>
+     */
+    private final Map<String, ClassDescriptor> fullyQualifiedNameToClassMap = new HashMap<>();
+
+    /**
+     * List of glob patterns for classes to exclude from the graph.
+     */
+    private final List<String> blacklistClassGlobs = new ArrayList<>();
+
+    /**
+     * List of glob patterns for classes to include in the graph.
+     *
+     * <p>If non-empty, only classes matching a whitelist pattern are shown.</p>
+     */
+    private final List<String> whitelistClassGlobs = new ArrayList<>();
+
+    /**
+     * The output image format for generated graphs.
+     *
+     * <p>SVG is recommended for large diagrams; PNG for small ones.</p>
+     */
+    TargetImageType targetImageType = TargetImageType.SVG;
+
+    /**
+     * Whether to keep the intermediate DOT file after rendering.
+     */
+    private boolean keepDotFile;
+
+    /**
+     * Directory where output files will be written.
+     *
+     * <p>Defaults to the current working directory.</p>
+     */
+    private File targetDirectory = new File(getProperty("user.dir") + separator);
+
+    /**
+     * Creates an empty class graph ready to accept classes.
+     */
+    public ClassGraph() {
+    }
+
+    /**
+     * Adds objects or classes to the graph for analysis.
+     *
+     * <p>Each object's class is analyzed and added to the graph.
+     * If a Class object is passed, the class itself is added.</p>
+     *
+     * @param objects objects or Class instances to add to the graph
+     * @return this ClassGraph instance for method chaining
+     */
+    public ClassGraph add(final Object... objects) {
+
+        if (objects != null)
+            for (final Object object : objects)
+                addObject(object);
+
+        return this;
+    }
+
+    /**
+     * Adds a single object's class to the graph.
+     *
+     * <p>If the object is a Class instance, that class is added directly.
+     * Otherwise, the object's runtime class is added.</p>
+     *
+     * @param object the object or Class to add
+     */
+    private void addObject(final Object object) {
+        if (object instanceof Class)
+            getOrCreateClassDescriptor((Class) object);
+        else
+            getOrCreateClassDescriptor(object.getClass());
+    }
+
+    /**
+     * Adds a glob pattern to the blacklist for filtering classes.
+     *
+     * <p>Classes matching any blacklist pattern are excluded from the graph.
+     * Glob patterns support wildcards: * matches any sequence, ? matches single characters.</p>
+     *
+     * <p>Example patterns:</p>
+     * <ul>
+     *   <li>"java.*" - excludes all java standard library</li>
+     *   <li>"*Test" - excludes all test classes</li>
+     * </ul>
+     *
+     * @param glob the glob pattern to add to the blacklist
+     */
+    public void blacklistClassGlob(final String glob) {
+        blacklistClassGlobs.add(glob);
+    }
+
+    /**
+     * Sets the output image format for graph rendering.
+     *
+     * @param targetImageType the image format (SVG or PNG)
+     */
+    public void setTargetImageType(TargetImageType targetImageType) {
+        this.targetImageType = targetImageType;
+    }
+
+    /**
+     * Generates the graph visualization as an image file.
+     *
+     * <p>This method:</p>
+     * <ol>
+     *   <li>Writes the DOT file to the target directory</li>
+     *   <li>Executes GraphViz (dot command) to render the image</li>
+     *   <li>Deletes the DOT file unless keepDotFile is true</li>
+     * </ol>
+     *
+     * <p>The GraphViz 'dot' executable must be available on the system PATH.</p>
+     *
+     * @param resultFileName the base name for output files (extension added automatically)
+     */
+    public void generateGraph(final String resultFileName) {
+
+        final File dotFile = new File(targetDirectory, resultFileName + ".dot");
+        final File imageFile = new File(targetDirectory, resultFileName + "." + targetImageType.fileExtension);
+
+        try {
+            // write DOT file to disk
+            final PrintWriter out = new PrintWriter(dotFile, "UTF-8");
+            out.write(getDot());
+            out.close();
+
+            // execute GraphViz to visualize graph
+            try {
+                Runtime.getRuntime()
+                        .exec(new String[]{"dot",
+                                "-T" + targetImageType.fileExtension,
+                                dotFile.getAbsolutePath(),
+                                "-o",
+                                imageFile.getAbsolutePath()}).waitFor();
+            } catch (final InterruptedException ignored) {
+            }
+
+            if (!keepDotFile)
+                // delete dot file
+                if (!dotFile.delete())
+                    throw new RuntimeException("Cannot delete file: " + dotFile.getAbsolutePath());
+
+        } catch (final IOException e) {
+            throw new RuntimeException("Unable to generate graph: " + e.getMessage(), e);
+        }
+
+    }
+
+    /**
+     * Generates the complete DOT format representation of the graph.
+     *
+     * <p>This creates a digraph with all class nodes and their relationships.</p>
+     *
+     * @return the DOT format string for the entire graph
+     */
+    private String getDot() {
+        final StringBuilder result = new StringBuilder();
+
+        result.append("digraph Java {\n");
+        result.append("graph [rankdir=LR, overlap = false, concentrate=true];\n");
+
+        for (final Map.Entry<String, ClassDescriptor> entry : fullyQualifiedNameToClassMap
+                .entrySet())
+            result.append(entry.getValue().getDot());
+
+        result.append("}\n");
+
+        return result.toString();
+    }
+
+    /**
+     * Creates or retrieves a class descriptor for a Java class.
+     *
+     * <p>If a descriptor for the class already exists in the graph, it is returned.
+     * Otherwise, a new descriptor is created, added to the map, and analyzed.</p>
+     *
+     * @param clazz the Java Class to get a descriptor for
+     * @return the ClassDescriptor for the class, or null if clazz is null
+     */
+    protected ClassDescriptor getOrCreateClassDescriptor(final Class clazz) {
+
+        if (clazz == null)
+            return null;
+
+        final String classFullyQualifiedName = clazz.getName();
+
+        // reuse existing instance if possible
+        if (fullyQualifiedNameToClassMap.containsKey(classFullyQualifiedName))
+            return fullyQualifiedNameToClassMap.get(classFullyQualifiedName);
+
+        // create new class descriptor
+        final ClassDescriptor newClassDescriptor = new ClassDescriptor(this);
+        fullyQualifiedNameToClassMap.put(classFullyQualifiedName,
+                newClassDescriptor);
+
+        newClassDescriptor.analyzeClass(clazz);
+
+        return newClassDescriptor;
+    }
+
+    /**
+     * Hides classes that have no incoming or outgoing references.
+     *
+     * <p>Orphaned classes add visual clutter without showing meaningful
+     * relationships. This method removes them from the graph output.</p>
+     *
+     * @return this ClassGraph instance for method chaining
+     */
+    public ClassGraph hideOrphanedClasses() {
+
+        for (final ClassDescriptor classDescriptor : fullyQualifiedNameToClassMap
+                .values())
+            classDescriptor.hideClassIfNoReferences();
+
+        return this;
+    }
+
+    /**
+     * Checks whether a class should be shown based on filter patterns.
+     *
+     * <p>A class is hidden if it matches any blacklist pattern, or if
+     * a whitelist exists and it doesn't match any whitelist pattern.</p>
+     *
+     * @param className the fully qualified class name to check
+     * @return true if the class passes the filter, false if it should be hidden
+     */
+    protected boolean isClassShown(final String className) {
+        for (final String pattern : blacklistClassGlobs)
+            if (GlobMatcher.match(className, pattern))
+                return false;
+
+        if (!whitelistClassGlobs.isEmpty()) {
+            for (final String pattern : whitelistClassGlobs)
+                if (GlobMatcher.match(className, pattern))
+                    return true;
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Sets whether to keep the intermediate DOT file after rendering.
+     *
+     * <p>Keeping the DOT file is useful for debugging or manual customization.</p>
+     *
+     * @param keepDotFile true to keep the DOT file, false to delete it
+     * @return this ClassGraph instance for method chaining
+     */
+    public ClassGraph setKeepDotFile(final boolean keepDotFile) {
+        this.keepDotFile = keepDotFile;
+
+        return this;
+    }
+
+    /**
+     * Sets the target directory for output files.
+     *
+     * @param targetDirectory the directory where DOT and image files will be written
+     * @return this ClassGraph instance for method chaining
+     */
+    public ClassGraph setTargetDirectory(File targetDirectory) {
+        this.targetDirectory = targetDirectory;
+        return this;
+    }
+
+    /**
+     * Adds a glob pattern to the whitelist for including classes.
+     *
+     * <p>If any whitelist patterns are defined, only classes matching at least
+     * one pattern will be shown in the graph.</p>
+     *
+     * @param glob the glob pattern to add to the whitelist
+     * @return this ClassGraph instance for method chaining
+     */
+    public ClassGraph whitelistClassGlob(final String glob) {
+        whitelistClassGlobs.add(glob);
+        return this;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/eu/svjatoslav/inspector/java/structure/FieldDescriptor.java b/src/main/java/eu/svjatoslav/inspector/java/structure/FieldDescriptor.java
new file mode 100755 (executable)
index 0000000..dc1d20e
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * JavaInspect - Utility to visualize java software.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+
+package eu.svjatoslav.inspector.java.structure;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a single field within a Java class for visualization purposes.
+ *
+ * <p>Field descriptors capture field names, types, and generic type arguments,
+ * generating GraphViz DOT format output for both embedded display within class
+ * nodes and external reference edges.</p>
+ *
+ * @see ClassDescriptor
+ * @see GraphElement
+ */
+public class FieldDescriptor implements GraphElement {
+
+    /**
+     * The parent class that contains this field.
+     */
+    private final ClassDescriptor parentClassDescriptor;
+
+    /**
+     * Generic type arguments for parameterized field types.
+     *
+     * <p>For example, a field of type {@code List<String>} would have
+     * String as a type argument.</p>
+     */
+    private final List<ClassDescriptor> typeArguments = new ArrayList<>();
+
+    /**
+     * The simple name of the field.
+     */
+    private String name;
+
+    /**
+     * The type descriptor for the field's declared type.
+     */
+    private ClassDescriptor type;
+
+    /**
+     * Whether this field is inherited from a parent class.
+     *
+     * <p>Inherited fields are typically hidden in visualizations.</p>
+     */
+    private boolean isInherited;
+
+    /**
+     * Creates a field descriptor associated with a parent class.
+     *
+     * @param parent the class descriptor that contains this field
+     */
+    public FieldDescriptor(final ClassDescriptor parent) {
+        parentClassDescriptor = parent;
+    }
+
+    /**
+     * Analyzes a Java reflection Field to populate this descriptor.
+     *
+     * <p>This method extracts the field name, type, and generic type arguments,
+     * registering references with the class graph for later visualization.</p>
+     *
+     * @param field the Java reflection Field to analyze
+     */
+    public void analyzeField(final Field field) {
+
+        if (!field.getDeclaringClass().getName()
+                .equals(parentClassDescriptor.getFullyQualifiedName()))
+            isInherited = true;
+
+        name = field.getName();
+        type = parentClassDescriptor.getClassGraph().getOrCreateClassDescriptor(
+                field.getType());
+        type.registerReference();
+
+        final Type fieldGenericType = field.getGenericType();
+        if (fieldGenericType instanceof ParameterizedType) {
+            final ParameterizedType fieldParameterizedGenericType = (ParameterizedType) fieldGenericType;
+            for (final Type type : fieldParameterizedGenericType.getActualTypeArguments())
+                if (type instanceof Class) {
+                    final Class aClass = (Class) type;
+                    final ClassDescriptor genericTypeDescriptor = parentClassDescriptor
+                            .getClassGraph().getOrCreateClassDescriptor(aClass);
+                    genericTypeDescriptor.registerReference();
+                    typeArguments.add(genericTypeDescriptor);
+                }
+
+        }
+    }
+
+    /**
+     * Generates DOT format edges showing references from this field to other classes.
+     *
+     * <p>This creates the arrows pointing from the field to its type and
+     * generic type arguments in the class diagram.</p>
+     *
+     * @return DOT format string for edges, or empty string if not visible
+     */
+    @Override
+    public String getDot() {
+
+        if (!isVisible())
+            return "";
+
+        final StringBuilder result = new StringBuilder();
+
+        // describe associated types
+        for (final ClassDescriptor classDescriptor : typeArguments)
+            if (classDescriptor.isVisible())
+                if (classDescriptor.areReferencesShown())
+                    result.append("    " + getGraphId() + " -> "
+                            + classDescriptor.getGraphId() + "[label=\"" + name
+                            + "\", color=\"" + classDescriptor.getColor()
+                            + "\", style=\"bold\"];\n");
+
+        if (type == null) return result.toString();
+        if (!type.isVisible())
+            return result.toString();
+
+        // main type
+        boolean showLink = type.areReferencesShown();
+
+        if (type == parentClassDescriptor)
+            showLink = false;
+
+        if (parentClassDescriptor.isEnum)
+            showLink = false;
+
+        if (showLink)
+            result.append("    " + getGraphId() + " -> " + type.getGraphId()
+                    + "[label=\"" + name + "\"," + " color=\""
+                    + type.getColor() + "\", style=\"bold\"];\n");
+
+        return result.toString();
+    }
+
+    /**
+     * Generates embedded DOT format HTML table row for display within the parent class node.
+     *
+     * <p>This creates a table row showing the field type and name inside
+     * the class box in the diagram.</p>
+     *
+     * @return DOT format HTML table row, or empty string if not visible
+     */
+    @Override
+    public String getEmbeddedDot() {
+
+        if (!isVisible())
+            return "";
+
+        final StringBuilder result = new StringBuilder();
+
+        result.append("        // " + name + "\n");
+        if (parentClassDescriptor.isEnum && (type == parentClassDescriptor)) {
+            result.append("        <TR><TD colspan=\"2\" PORT=\"" + name);
+            result.append("\" ALIGN=\"left\"><FONT POINT-SIZE=\"11.0\">");
+            result.append(name + "</FONT></TD></TR>\n");
+        } else {
+            result.append("        <TR><td ALIGN=\"right\">");
+            result.append("<FONT POINT-SIZE=\"8.0\">");
+            result.append(describeType() + "</FONT>");
+            result.append("</td><TD PORT=\"" + name);
+            result.append("\" ALIGN=\"left\"><FONT POINT-SIZE=\"11.0\">");
+            result.append(name + "</FONT></TD></TR>\n");
+        }
+        return result.toString();
+    }
+
+    /**
+     * Returns a human-readable description of the field's type.
+     *
+     * @return the type name with array indicator if applicable, or "-null-" if type is null
+     */
+    private String describeType() {
+        if (type == null) return "-null-";
+        return type.getClassName(true);
+    }
+
+    /**
+     * Returns the GraphViz identifier for this field.
+     *
+     * <p>The ID combines the parent class ID with the field name,
+     * allowing precise edge targeting in the diagram.</p>
+     *
+     * @return unique identifier in format "classId:fieldName"
+     */
+    @Override
+    public String getGraphId() {
+        return parentClassDescriptor.getGraphId() + ":" + name;
+    }
+
+    /**
+     * Counts visible external references from this field.
+     *
+     * <p>This is used to determine whether a class has enough connections
+     * to be worth displaying.</p>
+     *
+     * @return 1 if the field type is visible, 0 otherwise
+     */
+    protected int getOutsideVisibleReferencesCount() {
+
+        if (!isVisible())
+            return 0;
+
+        if (type != null)
+            if (type.isVisible())
+                return 1;
+
+        return 0;
+    }
+
+    /**
+     * Returns the type descriptor for this field.
+     *
+     * @return the ClassDescriptor representing the field's type
+     */
+    protected ClassDescriptor getType() {
+        return type;
+    }
+
+    /**
+     * Determines whether this field should be included in the visualization.
+     *
+     * <p>Fields are hidden if they are inherited, contain dollar signs
+     * (compiler-generated), or are serialVersionUID constants.</p>
+     *
+     * @return true if the field should be displayed, false otherwise
+     */
+    @Override
+    public boolean isVisible() {
+        if (isInherited)
+            return false;
+
+        if (name.contains("$"))
+            return false;
+
+        return !name.equals("serialVersionUID");
+
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/eu/svjatoslav/inspector/java/structure/GraphElement.java b/src/main/java/eu/svjatoslav/inspector/java/structure/GraphElement.java
new file mode 100755 (executable)
index 0000000..b5873d4
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * JavaInspect - Utility to visualize java software.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+
+package eu.svjatoslav.inspector.java.structure;
+
+/**
+ * Common interface for elements that can be rendered as GraphViz DOT nodes.
+ *
+ * <p>All graph elements (classes, fields, methods) implement this interface
+ * to provide DOT format output for visualization.</p>
+ *
+ * @see ClassDescriptor
+ * @see FieldDescriptor
+ * @see MethodDescriptor
+ */
+public interface GraphElement {
+
+    /**
+     * Generates the DOT format representation for external references.
+     *
+     * <p>This method produces the edges (arrows) connecting this element
+     * to other elements in the graph.</p>
+     *
+     * @return DOT format string for edges, or empty string if not visible
+     */
+    String getDot();
+
+    /**
+     * Generates the embedded DOT representation for display within a parent node.
+     *
+     * <p>This method produces the HTML-like table cells that appear inside
+     * the parent class node in the graph.</p>
+     *
+     * @return DOT format string for embedded content, or empty string if not visible
+     */
+    String getEmbeddedDot();
+
+    /**
+     * Returns the unique GraphViz identifier for this element.
+     *
+     * <p>The ID is used to reference this element in edges and node definitions.</p>
+     *
+     * @return unique identifier string in GraphViz format
+     */
+    String getGraphId();
+
+    /**
+     * Determines whether this element should be included in the graph output.
+     *
+     * <p>Visibility is controlled by various factors including system package
+     * filtering, inheritance status, and reference count thresholds.</p>
+     *
+     * @return true if the element should be rendered, false otherwise
+     */
+    boolean isVisible();
+
+}
\ No newline at end of file
diff --git a/src/main/java/eu/svjatoslav/inspector/java/structure/MethodDescriptor.java b/src/main/java/eu/svjatoslav/inspector/java/structure/MethodDescriptor.java
new file mode 100755 (executable)
index 0000000..0324469
--- /dev/null
@@ -0,0 +1,319 @@
+/*
+ * JavaInspect - Utility to visualize java software.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+
+package eu.svjatoslav.inspector.java.structure;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a single method within a Java class for visualization purposes.
+ *
+ * <p>Method descriptors capture method names, return types, and generic type
+ * information, generating GraphViz DOT format output for both embedded display
+ * within class nodes and external reference edges.</p>
+ *
+ * @see ClassDescriptor
+ * @see GraphElement
+ */
+public class MethodDescriptor implements GraphElement,
+        Comparable<MethodDescriptor> {
+
+    /**
+     * The simple name of the method.
+     */
+    private final String methodName;
+
+    /**
+     * The parent class that contains this method.
+     */
+    private final ClassDescriptor parentClass;
+
+    /**
+     * Type arguments extracted from generic return types.
+     *
+     * <p>For methods returning parameterized types like {@code List<String>},
+     * this captures the generic type arguments.</p>
+     */
+    private final List<ClassDescriptor> argumentTypes = new ArrayList<>();
+
+    /**
+     * The type descriptor for the method's return type.
+     */
+    private ClassDescriptor returnType;
+
+    /**
+     * Whether this method is inherited from a parent class or interface.
+     *
+     * <p>Inherited methods are typically hidden in visualizations to reduce noise.</p>
+     */
+    private boolean isInherited;
+
+    /**
+     * Creates a method descriptor associated with a parent class.
+     *
+     * @param parent the class descriptor that contains this method
+     * @param methodName the simple name of the method
+     */
+    public MethodDescriptor(final ClassDescriptor parent,
+                            final String methodName) {
+        parentClass = parent;
+        this.methodName = methodName;
+    }
+
+    /**
+     * Analyzes a Java reflection Method to populate this descriptor.
+     *
+     * <p>This method extracts the return type and generic type information,
+     * registering references with the class graph for later visualization.</p>
+     *
+     * @param method the Java reflection Method to analyze
+     */
+    public void analyze(final Method method) {
+
+        if (!method.getDeclaringClass().getName()
+                .equals(parentClass.getFullyQualifiedName()))
+            isInherited = true;
+
+        returnType = parentClass.getClassGraph().getOrCreateClassDescriptor(
+                method.getReturnType());
+        returnType.registerReference();
+
+        final Type genericType = method.getGenericReturnType();
+        if (genericType instanceof ParameterizedType) {
+            final ParameterizedType pt = (ParameterizedType) genericType;
+            for (final Type t : pt.getActualTypeArguments())
+                if (t instanceof Class) {
+                    final Class cl = (Class) t;
+                    final ClassDescriptor classDescriptor = parentClass
+                            .getClassGraph().getOrCreateClassDescriptor(cl);
+                    classDescriptor.registerReference();
+                    argumentTypes.add(classDescriptor);
+                }
+
+        }
+    }
+
+    /**
+     * Compares this method descriptor to another object for equality.
+     *
+     * <p>Equality is based on method name, parent class, and argument types.</p>
+     *
+     * @param o the object to compare
+     * @return true if the objects are equal
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof MethodDescriptor)) return false;
+
+        MethodDescriptor that = (MethodDescriptor) o;
+
+        if (methodName != null ? !methodName.equals(that.methodName) : that.methodName != null) return false;
+        if (parentClass != null ? !parentClass.equals(that.parentClass) : that.parentClass != null) return false;
+        return argumentTypes != null ? argumentTypes.equals(that.argumentTypes) : that.argumentTypes == null;
+
+    }
+
+
+    /**
+     * Returns a hash code for this method descriptor.
+     *
+     * @return hash code based on method name, parent class, and argument types
+     */
+    @Override
+    public int hashCode() {
+        int result = methodName != null ? methodName.hashCode() : 0;
+        result = 31 * result + (parentClass != null ? parentClass.hashCode() : 0);
+        result = 31 * result + (argumentTypes != null ? argumentTypes.hashCode() : 0);
+        return result;
+    }
+
+    /**
+     * Generates DOT format edges showing references from this method to other classes.
+     *
+     * <p>This creates dotted arrows pointing from the method to its return type
+     * and generic type arguments in the class diagram.</p>
+     *
+     * @return DOT format string for edges, or empty string if not visible
+     */
+    @Override
+    public String getDot() {
+
+        if (!isVisible())
+            return "";
+
+        final StringBuilder result = new StringBuilder();
+
+        // describe associated types
+        for (final ClassDescriptor classDescriptor : argumentTypes)
+            if (classDescriptor.isVisible())
+                if (classDescriptor.areReferencesShown())
+                    result.append("    " + getGraphId() + " -> "
+                            + classDescriptor.getGraphId() + "[label=\""
+                            + methodName + "\", color=\""
+                            + classDescriptor.getColor()
+                            + "\", style=\"dotted, bold\"];\n");
+
+        if (returnType == null) return result.toString();
+        if (!returnType.isVisible()) return result.toString();
+
+        // main type
+        if (returnType.areReferencesShown())
+            result.append("    " + getGraphId() + " -> "
+                    + returnType.getGraphId() + "[label=\"" + methodName
+                    + "\"," + " color=\"" + returnType.getColor()
+                    + "\", style=\"dotted, bold\"];\n");
+
+        return result.toString();
+    }
+
+    /**
+     * Generates embedded DOT format HTML table row for display within the parent class node.
+     *
+     * <p>This creates a table row showing the return type and method name
+     * inside the class box in the diagram. Method names are displayed in red.</p>
+     *
+     * @return DOT format HTML table row, or empty string if not visible
+     */
+    @Override
+    public String getEmbeddedDot() {
+        if (!isVisible())
+            return "";
+
+        final StringBuilder result = new StringBuilder();
+
+        result.append("        // " + methodName + "\n");
+
+        result.append("        <TR><td ALIGN=\"right\">"
+                + "<FONT POINT-SIZE=\"8.0\">" + describeReturnType()
+                + "</FONT>" + "</td><TD PORT=\"" + getMethodLabel()
+                + "\" ALIGN=\"left\"><FONT COLOR =\"red\" POINT-SIZE=\"11.0\">"
+                + getMethodLabel() + "</FONT></TD></TR>\n");
+
+        return result.toString();
+    }
+
+    /**
+     * Returns a human-readable description of the method's return type.
+     *
+     * @return the return type name with array indicator if applicable, or "-null-" if null
+     */
+    private String describeReturnType() {
+        if (returnType == null) return "-null-";
+
+        return returnType.getClassName(true);
+    }
+
+    /**
+     * Returns the GraphViz identifier for this method.
+     *
+     * <p>The ID combines the parent class ID with the method name.</p>
+     *
+     * @return unique identifier in format "classId:methodName"
+     */
+    @Override
+    public String getGraphId() {
+        return parentClass.getGraphId() + ":" + methodName;
+    }
+
+    /**
+     * Returns the label used for displaying the method in the diagram.
+     *
+     * @return the method name
+     */
+    private String getMethodLabel() {
+        return methodName;
+    }
+
+    /**
+     * Counts visible external references from this method.
+     *
+     * <p>This counts both the return type and any generic type arguments
+     * that are visible in the graph.</p>
+     *
+     * @return the number of visible referenced types
+     */
+    protected int getOutsideVisibleReferencesCount() {
+        int result = 0;
+
+        if (returnType != null)
+            if (returnType.isVisible())
+                result++;
+
+        for (final ClassDescriptor classDescriptor : argumentTypes)
+            if (classDescriptor.isVisible())
+                result++;
+
+        return result;
+    }
+
+    /**
+     * Determines whether this method should be included in the visualization.
+     *
+     * <p>Methods are hidden if they are:</p>
+     * <ul>
+     *   <li>Inherited from parent classes</li>
+     *   <li>Common Object methods (wait, equals, toString, etc.)</li>
+     *   <li>Enum methods (values, valueOf, etc.) for enum classes</li>
+     *   <li>Getter/setter methods that correspond to existing fields</li>
+     * </ul>
+     *
+     * @return true if the method should be displayed, false otherwise
+     */
+    @Override
+    public boolean isVisible() {
+
+        // hide inherited methods
+        if (isInherited)
+            return false;
+
+        // hide common object methods
+        if (Utils.isCommonObjectMethod(methodName))
+            return false;
+
+        // hide common Enumeration methods
+        if (parentClass.isEnum && Utils.isEnumMethod(methodName))
+            return false;
+
+        // hide get/set methods for the field of the same name
+        if (methodName.startsWith("get") || methodName.startsWith("set"))
+            if (parentClass.hasFieldIgnoreCase(methodName.substring(3)))
+                return false;
+
+        // hide is methods for the boolean field of the same name
+        if (methodName.startsWith("is")) {
+            final FieldDescriptor field = parentClass
+                    .getFieldIgnoreCase(methodName.substring(2));
+            if (field != null)
+                if ("boolean".equals(field.getType().getFullyQualifiedName()))
+                    return false;
+        }
+
+        return true;
+
+    }
+
+    /**
+     * Compares this method descriptor to another for sorting purposes.
+     *
+     * <p>Methods are sorted primarily by name, then by parent class.</p>
+     *
+     * @param that the other method descriptor to compare
+     * @return negative if this comes before that, positive if after, zero if equal
+     */
+    @Override
+    public int compareTo(MethodDescriptor that) {
+        if (this == that) return 0;
+
+        int comparisonResult = methodName.compareTo(that.methodName);
+        if (comparisonResult != 0) return comparisonResult;
+
+        return parentClass.compareTo(that.parentClass);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/eu/svjatoslav/inspector/java/structure/TargetImageType.java b/src/main/java/eu/svjatoslav/inspector/java/structure/TargetImageType.java
new file mode 100644 (file)
index 0000000..80e7fc9
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * JavaInspect - Utility to visualize java software.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+
+package eu.svjatoslav.inspector.java.structure;
+
+/**
+ * Supported output image formats for class graph visualization.
+ *
+ * <p>JavaInspect uses GraphViz to generate visual representations of
+ * class relationships. This enum defines the available output formats.</p>
+ *
+ * @see ClassGraph#setTargetImageType(TargetImageType)
+ */
+public enum TargetImageType {
+
+    /**
+     * Portable Network Graphics format.
+     *
+     * <p>A raster image format suitable for embedding in documents
+     * and web pages where scalability is not required.</p>
+     */
+    PNG("png"),
+
+    /**
+     * Scalable Vector Graphics format.
+     *
+     * <p>A vector image format that scales infinitely without quality loss,
+     * ideal for large class diagrams and high-resolution displays.</p>
+     */
+    SVG("svg");
+
+    /**
+     * The file extension used for this image format.
+     */
+    public final String fileExtension;
+
+    /**
+     * Creates a target image type with the specified file extension.
+     *
+     * @param fileExtension the file extension without the leading dot
+     */
+    TargetImageType(String fileExtension) {
+        this.fileExtension = fileExtension;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/eu/svjatoslav/inspector/java/structure/Utils.java b/src/main/java/eu/svjatoslav/inspector/java/structure/Utils.java
new file mode 100755 (executable)
index 0000000..0419aa8
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * JavaInspect - Utility to visualize java software.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+
+package eu.svjatoslav.inspector.java.structure;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility class providing filtering and styling helpers for class graph generation.
+ *
+ * <p>This class maintains static collections of system data types, common methods,
+ * system packages, and color palettes used throughout the visualization process.</p>
+ *
+ * <p>All methods are static and the class is not intended to be instantiated.</p>
+ *
+ * @see ClassDescriptor
+ * @see MethodDescriptor
+ */
+public class Utils {
+
+    /**
+     * List of primitive and system data type names to exclude from visualization.
+     */
+    private static final List<String> systemDataTypes = new ArrayList<>();
+
+    /**
+     * List of common {@link Object} method names to hide in method listings.
+     */
+    private static final List<String> commonObjectMethods = new ArrayList<>();
+
+    /**
+     * List of system package prefixes to exclude from visualization.
+     */
+    private static final List<String> systemPackages = new ArrayList<>();
+
+    /**
+     * Palette of dark colors for reference edges (incoming arrows).
+     */
+    private static final List<String> darkColors = new ArrayList<>();
+
+    /**
+     * Palette of light colors for inheritance edges (superclass arrows).
+     */
+    private static final List<String> lightColors = new ArrayList<>();
+
+    /**
+     * List of enum-specific method names to hide for enum classes.
+     */
+    private static final List<String> enumMethods = new ArrayList<>();
+
+    /**
+     * Current index in the dark color palette for cyclic selection.
+     */
+    private static int lastChosenDarkColor = -1;
+
+    /**
+     * Current index in the light color palette for cyclic selection.
+     */
+    private static int lastChosenLightColor = -1;
+
+    /**
+     * Static initializer that populates all filter and color lists.
+     */
+    static {
+        initEnumMethods();
+        initSystemDataTypes();
+        initDarkColors();
+        initLightColors();
+        initCommonObjectMethods();
+        initSystemPackages();
+    }
+
+    /**
+     * Returns the next color from the dark color palette.
+     *
+     * <p>Colors are selected cyclically, wrapping around to the first color
+     * after the last one has been used.</p>
+     *
+     * @return the name of the next dark color for GraphViz styling
+     */
+    protected static String getNextDarkColor() {
+        lastChosenDarkColor++;
+        if (lastChosenDarkColor >= darkColors.size())
+            lastChosenDarkColor = 0;
+
+        return darkColors.get(lastChosenDarkColor);
+    }
+
+    /**
+     * Returns the next color from the light color palette.
+     *
+     * <p>Colors are selected cyclically, wrapping around to the first color
+     * after the last one has been used.</p>
+     *
+     * @return the name of the next light color for GraphViz styling
+     */
+    protected static String getNextLightColor() {
+        lastChosenLightColor++;
+        if (lastChosenLightColor >= lightColors.size())
+            lastChosenLightColor = 0;
+
+        return lightColors.get(lastChosenLightColor);
+    }
+
+    /**
+     * Populates the list of common Object methods to filter out.
+     *
+     * <p>These methods (wait, equals, toString, etc.) are inherited by all
+     * objects and typically add noise to class diagrams.</p>
+     */
+    private static void initCommonObjectMethods() {
+        commonObjectMethods.add("wait");
+        commonObjectMethods.add("equals");
+        commonObjectMethods.add("toString");
+        commonObjectMethods.add("hashCode");
+        commonObjectMethods.add("notify");
+        commonObjectMethods.add("notifyAll");
+        commonObjectMethods.add("getClass");
+    }
+
+    /**
+     * Populates the dark color palette for reference edges.
+     *
+     * <p>Dark colors are used for incoming reference arrows to distinguish
+     * them from inheritance relationships.</p>
+     */
+    protected static void initDarkColors() {
+        darkColors.add("antiquewhite4");
+        darkColors.add("blueviolet");
+        darkColors.add("brown4");
+        darkColors.add("chartreuse4");
+        darkColors.add("cyan4");
+        darkColors.add("deeppink1");
+        darkColors.add("deepskyblue3");
+        darkColors.add("firebrick1");
+        darkColors.add("goldenrod3");
+        darkColors.add("gray0");
+    }
+
+    /**
+     * Populates the list of enum-specific methods to filter out.
+     *
+     * <p>These methods (values, valueOf, name, etc.) are present in all
+     * enum classes and are typically not relevant for visualization.</p>
+     */
+    private static void initEnumMethods() {
+        enumMethods.add("values");
+        enumMethods.add("valueOf");
+        enumMethods.add("name");
+        enumMethods.add("compareTo");
+        enumMethods.add("valueOf");
+        enumMethods.add("getDeclaringClass");
+        enumMethods.add("ordinal");
+    }
+
+    /**
+     * Populates the light color palette for inheritance edges.
+     *
+     * <p>Light colors are used for superclass arrows to visually distinguish
+     * inheritance relationships from field/method references.</p>
+     */
+    private static void initLightColors() {
+        lightColors.add("olivedrab2");
+        lightColors.add("peachpuff2");
+        lightColors.add("seagreen1");
+        lightColors.add("violet");
+        lightColors.add("cyan");
+        lightColors.add("orange");
+    }
+
+    /**
+     * Populates the list of primitive and system data types to filter out.
+     *
+     * <p>These types (int, long, boolean, etc.) are fundamental and do not
+     * represent interesting relationships in class diagrams.</p>
+     */
+    private static void initSystemDataTypes() {
+        systemDataTypes.add("void");
+        systemDataTypes.add("int");
+        systemDataTypes.add("long");
+        systemDataTypes.add("float");
+        systemDataTypes.add("double");
+        systemDataTypes.add("boolean");
+        systemDataTypes.add("char");
+        systemDataTypes.add("short");
+        systemDataTypes.add("byte");
+    }
+
+    /**
+     * Populates the list of system package prefixes to filter out.
+     *
+     * <p>Classes from these packages (java.*, javax.*, sun.*) are standard
+     * library classes and typically excluded from application diagrams.</p>
+     */
+    private static void initSystemPackages() {
+        systemPackages.add("java.");
+        systemPackages.add("javax.");
+        systemPackages.add("sun.");
+    }
+
+    /**
+     * Checks if a method name is a common Object method.
+     *
+     * @param name the method name to check
+     * @return true if the method is a common Object method like equals or toString
+     */
+    protected static boolean isCommonObjectMethod(final String name) {
+        return commonObjectMethods.contains(name);
+    }
+
+    /**
+     * Checks if a method name is an enum-specific method.
+     *
+     * @param name the method name to check
+     * @return true if the method is an enum method like values or valueOf
+     */
+    protected static boolean isEnumMethod(final String name) {
+        return enumMethods.contains(name);
+    }
+
+    /**
+     * Checks if a type name is a primitive or system data type.
+     *
+     * @param name the fully qualified type name to check
+     * @return true if the type is a primitive like int, boolean, or void
+     */
+    protected static boolean isSystemDataType(final String name) {
+        return systemDataTypes.contains(name);
+    }
+
+    /**
+     * Checks if a class belongs to a system package.
+     *
+     * <p>System packages include java.*, javax.*, and sun.* prefixes.</p>
+     *
+     * @param name the fully qualified class name to check
+     * @return true if the class is from a system package
+     */
+    protected static boolean isSystemPackage(final String name) {
+
+        for (final String packagePrefix : systemPackages)
+            if (name.startsWith(packagePrefix))
+                return true;
+
+        return false;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/eu/svjatoslav/inspector/java/structure/package-info.java b/src/main/java/eu/svjatoslav/inspector/java/structure/package-info.java
new file mode 100755 (executable)
index 0000000..6eab749
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * JavaInspect - Utility to visualize java software.
+ * This project is released under Creative Commons Zero (CC0) license.
+ */
+
+/**
+ * Provides classes for inspecting Java application structure through runtime reflection.
+ *
+ * <p>This package contains the core classes that traverse class references,
+ * extract field and method information, and generate GraphViz DOT format
+ * visualizations of Java class relationships.</p>
+ *
+ * @see eu.svjatoslav.inspector.java.structure.ClassGraph
+ * @see eu.svjatoslav.inspector.java.structure.ClassDescriptor
+ * @see eu.svjatoslav.inspector.java.structure.FieldDescriptor
+ * @see eu.svjatoslav.inspector.java.structure.MethodDescriptor
+ */
+package eu.svjatoslav.inspector.java.structure;
\ No newline at end of file
diff --git a/usage examples/demo project/.gitignore b/usage examples/demo project/.gitignore
new file mode 100644 (file)
index 0000000..49ea519
--- /dev/null
@@ -0,0 +1,4 @@
+/.idea/
+/target/
+/example.svg
+/*.iml
\ No newline at end of file
diff --git a/usage examples/demo project/pom.xml b/usage examples/demo project/pom.xml
new file mode 100644 (file)
index 0000000..6a16ce8
--- /dev/null
@@ -0,0 +1,40 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>eu.svjatoslav</groupId>
+    <artifactId>javainspect-demo</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>jar</packaging>
+    <name>JavaInspect demo</name>
+    <description>Demonstration project for JavaInspect utility</description>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>2.3.2</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                    <encoding>UTF-8</encoding>
+                </configuration>
+            </plugin>
+               </plugins>
+       </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>eu.svjatoslav</groupId>
+            <artifactId>javainspect</artifactId>
+            <version>1.7-SNAPSHOT</version>
+        </dependency>
+    </dependencies>
+
+    <repositories>
+        <repository>
+            <id>svjatoslav.eu</id>
+            <name>Svjatoslav repository</name>
+            <url>http://www2.svjatoslav.eu/maven/</url>
+        </repository>
+    </repositories>
+</project>
diff --git a/usage examples/demo project/src/main/java/eu/svjatoslav/inspector/java/structure/example/RenderUsingReflection.java b/usage examples/demo project/src/main/java/eu/svjatoslav/inspector/java/structure/example/RenderUsingReflection.java
new file mode 100755 (executable)
index 0000000..d471429
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * JavaInspect - Utility to visualize java software
+ * Copyright (C) 2013-2020, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 3 of the GNU Lesser General Public License
+ * or later as published by the Free Software Foundation.
+ */
+
+package eu.svjatoslav.inspector.java.structure.example;
+
+import eu.svjatoslav.inspector.java.structure.ClassGraph;
+import eu.svjatoslav.inspector.java.structure.example.torender.SampleClass;
+import eu.svjatoslav.inspector.java.structure.example.torender.SampleClass2;
+
+public class RenderUsingReflection {
+
+    /**
+     * If you run this method using IDE, then example.svg file shall appear in project root directory.
+     */
+    public static void main(final String[] args) {
+
+        new ClassGraph().add(SampleClass.class, SampleClass2.class)
+                .generateGraph("example");
+    }
+
+}
diff --git a/usage examples/demo project/src/main/java/eu/svjatoslav/inspector/java/structure/example/torender/ObjectReturnedByMethod.java b/usage examples/demo project/src/main/java/eu/svjatoslav/inspector/java/structure/example/torender/ObjectReturnedByMethod.java
new file mode 100755 (executable)
index 0000000..143ab77
--- /dev/null
@@ -0,0 +1,5 @@
+package eu.svjatoslav.inspector.java.structure.example.torender;
+
+public class ObjectReturnedByMethod {
+
+}
diff --git a/usage examples/demo project/src/main/java/eu/svjatoslav/inspector/java/structure/example/torender/ObjectVisibleAsClassField.java b/usage examples/demo project/src/main/java/eu/svjatoslav/inspector/java/structure/example/torender/ObjectVisibleAsClassField.java
new file mode 100755 (executable)
index 0000000..db5b5cc
--- /dev/null
@@ -0,0 +1,5 @@
+package eu.svjatoslav.inspector.java.structure.example.torender;
+
+public class ObjectVisibleAsClassField {
+
+}
diff --git a/usage examples/demo project/src/main/java/eu/svjatoslav/inspector/java/structure/example/torender/SampleClass.java b/usage examples/demo project/src/main/java/eu/svjatoslav/inspector/java/structure/example/torender/SampleClass.java
new file mode 100755 (executable)
index 0000000..9720c44
--- /dev/null
@@ -0,0 +1,11 @@
+package eu.svjatoslav.inspector.java.structure.example.torender;
+
+public class SampleClass extends SampleSuperClass {
+
+    ObjectVisibleAsClassField sampleClassField;
+
+    public ObjectReturnedByMethod sampleMethod() {
+        return new ObjectReturnedByMethod();
+    }
+
+}
diff --git a/usage examples/demo project/src/main/java/eu/svjatoslav/inspector/java/structure/example/torender/SampleClass2.java b/usage examples/demo project/src/main/java/eu/svjatoslav/inspector/java/structure/example/torender/SampleClass2.java
new file mode 100644 (file)
index 0000000..a2eba71
--- /dev/null
@@ -0,0 +1,5 @@
+package eu.svjatoslav.inspector.java.structure.example.torender;
+
+public class SampleClass2 extends SampleSuperClass {
+
+}
diff --git a/usage examples/demo project/src/main/java/eu/svjatoslav/inspector/java/structure/example/torender/SampleEnum.java b/usage examples/demo project/src/main/java/eu/svjatoslav/inspector/java/structure/example/torender/SampleEnum.java
new file mode 100755 (executable)
index 0000000..ee8bf9e
--- /dev/null
@@ -0,0 +1,7 @@
+package eu.svjatoslav.inspector.java.structure.example.torender;
+
+public enum SampleEnum {
+
+    ONE, TWO, THREE, FOUR
+
+}
diff --git a/usage examples/demo project/src/main/java/eu/svjatoslav/inspector/java/structure/example/torender/SampleInterface.java b/usage examples/demo project/src/main/java/eu/svjatoslav/inspector/java/structure/example/torender/SampleInterface.java
new file mode 100755 (executable)
index 0000000..648327f
--- /dev/null
@@ -0,0 +1,5 @@
+package eu.svjatoslav.inspector.java.structure.example.torender;
+
+public interface SampleInterface {
+    SampleEnum getSomeValue();
+}
diff --git a/usage examples/demo project/src/main/java/eu/svjatoslav/inspector/java/structure/example/torender/SampleSuperClass.java b/usage examples/demo project/src/main/java/eu/svjatoslav/inspector/java/structure/example/torender/SampleSuperClass.java
new file mode 100755 (executable)
index 0000000..9230509
--- /dev/null
@@ -0,0 +1,10 @@
+package eu.svjatoslav.inspector.java.structure.example.torender;
+
+public class SampleSuperClass implements SampleInterface {
+
+    @Override
+    public SampleEnum getSomeValue() {
+        return SampleEnum.ONE;
+    }
+
+}
diff --git a/usage examples/demo project/tools/open with IntelliJ IDEA b/usage examples/demo project/tools/open with IntelliJ IDEA
new file mode 100755 (executable)
index 0000000..1f82875
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+cd "${0%/*}"
+
+cd ..
+idea .