{"id":22327144,"url":"https://github.com/msgbuf/msgbuf","last_synced_at":"2026-04-17T10:31:54.178Z","repository":{"id":57730517,"uuid":"367922225","full_name":"msgbuf/msgbuf","owner":"msgbuf","description":"Code generator for GWT-compatible Java data classes suitable for typed client-server messaging","archived":false,"fork":false,"pushed_at":"2026-03-26T08:04:33.000Z","size":3559,"stargazers_count":0,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-26T14:11:57.841Z","etag":null,"topics":["dart","data-class","gwt","java","protocol-buffers"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/msgbuf.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-05-16T15:42:43.000Z","updated_at":"2026-03-26T08:04:36.000Z","dependencies_parsed_at":"2025-04-29T14:41:14.033Z","dependency_job_id":"0b8cd2ee-5b0a-4f18-9d6c-5f3a07e7de67","html_url":"https://github.com/msgbuf/msgbuf","commit_stats":null,"previous_names":["haumacher/msgbuf"],"tags_count":21,"template":false,"template_full_name":null,"purl":"pkg:github/msgbuf/msgbuf","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/msgbuf%2Fmsgbuf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/msgbuf%2Fmsgbuf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/msgbuf%2Fmsgbuf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/msgbuf%2Fmsgbuf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/msgbuf","download_url":"https://codeload.github.com/msgbuf/msgbuf/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/msgbuf%2Fmsgbuf/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31925340,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-17T10:19:20.377Z","status":"ssl_error","status_checked_at":"2026-04-17T10:19:18.682Z","response_time":62,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dart","data-class","gwt","java","protocol-buffers"],"created_at":"2024-12-04T03:08:29.789Z","updated_at":"2026-04-17T10:31:54.171Z","avatar_url":"https://github.com/msgbuf.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# msgbuf\nCode generator for GWT-compatible Java data classes suitable for typed client-server messaging.\n\nInspired by Google's [protocol buffers](https://developers.google.com/protocol-buffers), `msgbuf` provides a code generator that produces data classes out of a concise protocol definition file. \n\nIn contrast to `protobuf`, `msgbuf` supports:\n * Code generation compatible with the [GWT Java-to-Javascript compiler](http://www.gwtproject.org/).\n * Inheritance of data classes.\n * Abstract data classes defining the root of a hierarchy of exchangeable data fragments.\n * Polymorphic data compositions.\n * [Visitor pattern](https://en.wikipedia.org/wiki/Visitor_pattern) for processing polymorphic data structures.\n \n`msgbuf` serializes messages in JSON, Binary, and XML formats. For GWT-compatibility it uses a modified `JsonReader`/`JsonWriter` from the [gson library](https://github.com/google/gson) that was abstracted from the unsupported `Reader`/`Writer` Java API.\n\n## Setup with Maven\n\n### Add the MsgBuf runtime library dependency to your project\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ede.haumacher.msgbuf\u003c/groupId\u003e\n    \u003cartifactId\u003emsgbuf-api\u003c/artifactId\u003e\n    \u003cversion\u003e1.2.1\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n### Add the MsgBuf generator to your pom.xml\n\nTo the `build/plugins` section add:\n\n```xml\n\u003cplugin\u003e\n    \u003cgroupId\u003ede.haumacher.msgbuf\u003c/groupId\u003e\n    \u003cartifactId\u003emsgbuf-generator-maven-plugin\u003c/artifactId\u003e\n    \u003cversion\u003e1.2.1\u003c/version\u003e\n    \n    \u003cexecutions\u003e\n        \u003cexecution\u003e\n            \u003cid\u003egenerate-protocols\u003c/id\u003e\n            \u003cgoals\u003e\n                \u003cgoal\u003egenerate\u003c/goal\u003e\n            \u003c/goals\u003e\n        \u003c/execution\u003e\n    \u003c/executions\u003e\n\u003c/plugin\u003e\n```\n\nNow you are ready to create `*.proto` files in your source folder and build them with `mvn compile`.\n\n### Where to place proto files\n\nProto files are placed in your Java source folder (`src/main/java/`) inside the directory matching their `package` declaration. The generated Java files are written next to the proto file. For example, a proto file with `package my.app.model;` should be placed at `src/main/java/my/app/model/shape.proto`, and the generated classes will appear in `src/main/java/my/app/model/`.\n\n## Usage\n \nThe `msgbuf` definition language is an extension of the [proto format](https://developers.google.com/protocol-buffers/docs/proto3) from `protobuf`. A defined message can `extend` another message type, or it can be marked `abstract`. \n\nAssume, you want to describe shapes in a graphics application, you could define the data types as follows. You may start with an `abstract` shape class, defining coordinates of the origin of its coordinate system:\n\nAdd `src/main/java/my/app/model/shape.proto` with the following contents:\n```protobuf\npackage my.app.model;\n\nabstract message Shape {\n  int32 xCoordinate;\n  int32 yCoordinate;\n}\n```\n\nBased on that, you create concrete classes for circles and rectangles:\n\n```protobuf\nmessage Circle extends Shape {\n  int32 radius;\n}\n\nmessage Rectangle extends Shape {\n  int32 width;\n  int32 height;\n}\n```\n\nFinally, you could create a group class that allows combining arbitrary shapes by placing them into a new coordinate system:\n\n```protobuf\nmessage Group extends Shape {\n  repeated Shape shapes;\n}\n```\n\nPassing these definitions to the `msgbuf` compiler gives you a class hierarchy with classes `Shape`, `Circle`, `Rectangle`, and `Group`. You can inspect the generation result in the test package [test.hierarchy](https://github.com/msgbuf/msgbuf/tree/main/de.haumacher.msgbuf.generator/src/test/java/test/hierarchy/data) of the compiler. The source of the example data class definitions can be seen in the [hierarchy.proto](https://github.com/msgbuf/msgbuf/tree/main/de.haumacher.msgbuf.generator/src/test/java/test/hierarchy/data/hierarchy.proto) file.\n\n### Enum types\n\nEnums define a fixed set of named constants. Each constant is terminated with a semicolon. Constants can optionally\nhave explicit numeric IDs assigned:\n\n```protobuf\nenum Corpus {\n    UNIVERSAL = 0;\n    WEB = 1;\n    IMAGES = 2;\n}\n```\n\nEnums can also be nested inside messages:\n\n```protobuf\nmessage SearchRequest {\n    enum Corpus {\n        UNIVERSAL = 0;\n        WEB = 1;\n    }\n    Corpus corpus;\n}\n```\n\n### Nested messages\n\nMessages can be nested inside other messages:\n\n```protobuf\nmessage SearchResponse {\n    message Result {\n        string url;\n        string title;\n        repeated string snippets;\n    }\n    repeated Result results;\n}\n```\n\n### Transient fields\n\nFields marked with `transient` are not serialized. They exist only in the in-memory representation:\n\n```protobuf\nmessage A {\n    string name;\n    transient string cachedValue;\n}\n```\n\n### Backtick-quoted identifiers\n\nBackticks can be used to escape identifiers that clash with msgbuf keywords:\n\n```protobuf\nmessage NullableValues {\n    @Nullable\n    int `int`;\n    @Nullable\n    boolean `boolean`;\n}\n```\n\n### Primitive type aliases\n\nIn addition to the protobuf type names, `msgbuf` supports shorter aliases:\n- `int` for `int32`\n- `long` for `int64`\n- `boolean` for `bool`\n\n### Native JSON value type (`json`)\n\nThe `json` type represents an opaque JSON value — any valid JSON structure (object, array, string, number, boolean, or null). This avoids double-serialization when a message needs to carry dynamically-typed data:\n\n```protobuf\nmessage PatchEvent {\n    string controlId;\n    json patch;\n}\n```\n\nIn JSON format, the value is written natively without string-wrapping:\n\n```json\n{\"controlId\":\"c1\",\"patch\":{\"nodes\":{\"n1\":{\"x\":100}}}}\n```\n\nThe generated Java API uses `Object` as the field type. Supported value types are `Map\u003cString, Object\u003e`, `List\u003cObject\u003e`, `String`, `Long`, `Double`, `Boolean`, and `null`:\n\n```java\nMap\u003cString, Object\u003e patch = new LinkedHashMap\u003c\u003e();\npatch.put(\"nodes\", ...);\nPatchEvent event = PatchEvent.create()\n    .setControlId(\"c1\")\n    .setPatch(patch);\n\n// Reading back:\nObject value = event.getPatch(); // Map, List, String, Number, Boolean, or null\n```\n\nFor binary serialization, json values are wrapped in a `JsonValue` message envelope (defined in `de.haumacher.msgbuf.json.value`). For XML, json values are encoded as JSON strings in element text.\n\n## Global protocol options\n\n### `option NoJson`\nDisables generation of read and write methods for the JSON format.\n\n### `option NoBinary`\nDisables generation of read and write methods for binary format.\n\n### `option NoXml`\nDisables generation of read and write methods for XML format.\n\n### `option NoXmlNames`\nDisables generation of constants for the XML format.\n\n### `option NoInterfaces`\nDisables generation interfaces for data classes. Normally, data classes are represented by a Java interface. This\nenables multiple inheritance for data classes. To reduce the amount of generated code, this can be disabled for simple\ncases, where no multiple inheritance is required.\n\n### `option NoListener`\nDisables generation of listener interfaces and corresponding registration methods. Add this options, if observing \ndata classes for changes is not required.\n\n### `option NoReflection`\nDisables generation of reflective access methods that allow access to properties through their property names.\n\n### `option NoVisitor`\nDisables generation of visitor interfaces and visit methods. \n\n### `option NoVisitorExceptions`\nProduces visitor interfaces that cannot throw declared exceptions.\n\n### `option NoTypeKind`\nSuppresses the type kind enumeration for a data class hierarchy.\n\n### `option SharedGraph`\nAllows to handle multiple synchronized instances of a data class graph. Each graph can be observed for changes. Changes\ngenerate synchronization messages to keep other instances of the same shared graph up to date. With this option,\na shared graph can be instantiated on a server, transferred to a client while keeping the state in sync when changes\noccur on each side.\n\n### `option UnorderedMaps`\nUses `HashMap` instead of `LinkedHashMap` for map-type properties. By default, map properties use `LinkedHashMap` to\npreserve insertion order. This option switches to `HashMap` for better performance when insertion order is not important.\n\nExample:\n```protobuf\noption UnorderedMaps;  // Use HashMap for better performance\n\nmessage Config {\n    map\u003cstring, string\u003e settings;  // Will use HashMap instead of LinkedHashMap\n}\n```\n\n### `option OpenWorld`\nEnables cross-file protocol extension for abstract type hierarchies. With this option, subtypes can be defined in\nseparate `.proto` files (and separate modules) using `import` and `extends`:\n\n```protobuf\n// base-module: events.proto\noption OpenWorld;\n\nabstract message Event {\n    long timestamp;\n}\n\nmessage TextEvent extends Event {\n    string text;\n}\n```\n\n```protobuf\n// extension-module: graph-events.proto\nimport \"../base-module/events.proto\";\n\nmessage GraphPatchEvent extends base.pkg.Event {\n    string controlId;\n    string patch;\n}\n```\n\nExtension types are discovered at runtime via `ServiceLoader`. The generator produces a registration class\n(e.g. `GraphEventsTypes`) and a `META-INF/services` descriptor automatically when the `-resources` output\ndirectory is configured. On platforms without `ServiceLoader` (e.g. GWT), call `GraphEventsTypes.init()` explicitly.\n\nThe base module's `.proto` files should be packaged as resources in its JAR (e.g. in `src/main/resources`).\nThe Maven plugin automatically resolves imports from compile-scope dependency JARs, so no file system paths\nto the base module are needed.\n\nImplications:\n- Implies `option NoBinary` (only JSON and XML serialization are supported)\n- Cannot be combined with `option NoInterfaces`\n- The generated `Visitor` interface includes a `visitDefault()` fallback method for unknown extension types\n- Extension types generate their own `Visitor` sub-interface with an `instanceof`-based dispatch\n\nMaven plugin configuration (extension module):\n```xml\n\u003cconfiguration\u003e\n    \u003cresourceOutputDir\u003e${project.basedir}/src/main/resources\u003c/resourceOutputDir\u003e\n\u003c/configuration\u003e\n```\n\nImports are resolved automatically from the compile classpath. If the base module packages its `.proto` files\nas resources, no additional configuration is needed beyond declaring the dependency.\n\nCLI usage (standalone, without Maven):\n```\njava -jar msgbuf-generator.jar -out src/main/java -resources src/main/resources -cp base-module.jar extension.proto\n```\n\nThe `-cp` option specifies JARs to search for imported `.proto` files. The `-I` option can still be used\nfor file system include paths.\n\n## Message options\n\n### Mix-in interfaces (`@Operations(...)`)\n\nThe data classes can extends mix-in interfaces with operations.\n\n```protobuf\n/** The data class */\n@Operations(\"test.operations.DataOperations\")\nmessage Data {\n  int x;\n}\n```\n```java\n/** The mix-in interface with operations on data. */\npublic interface DataOperations {\n    /** Access to the data. */\n    Data self();\n    \n    /** Operation added to data class. */\n    default void inc() {\n        self().setX(self().getX() + 1);\n    }\n}\n\n/** Testing the mix-in operation. */\npublic void testOperations() {\n    Data data = Data.create();\n    data.inc();\n    data.inc();\n    assertEquals(2, data.getX());\n}\n```\n\n## Property options\n\n### `@Nullable`\nA property of a primitive type that does not allow `null` values (e.g. `int` and `string`) can be explicitly marked to\nallow `null` values.\n\n### `@Singular(\"item\")`\nSpecifies the singular form for a repeated field, used when generating `addXxx()` and `removeXxx()` methods. This annotation\ntakes precedence over the automatic pluralization heuristics. Useful for irregular plurals or when the heuristics produce\nincorrect results.\n\nExample:\n```protobuf\nmessage Container {\n    @Singular(\"person\")\n    repeated string people;      // Generates addPerson() instead of addPeople()\n\n    @Singular(\"child\")\n    repeated string children;    // Generates addChild() instead of addChildren()\n\n    @Singular(\"datum\")\n    repeated string data;        // Generates addDatum() instead of addData()\n}\n```\n\n### `@Name(\"myProp\")`\nSets a custom property name. This name is used in JSON serialization.\n\n### `@XmlName(\"myProp\")`\nSets a custom tag name for XML serialization.\n\n### `@Reverse(\"otherProp\")`\nMarks a reference to be the reverse end of the reference with the given name in the target type.\n\n### `@Container`\nMarks a reference point to the container of the current object.\n\n### `@Ref`\nMarks a reference as cross reference (non-composition). When setting values to fields marked as cross reference, container properties are not updated.\n\n### `@type_id(4711)`\nSets a custom type discriminator ID for binary serialization of polymorphic hierarchies.\n\n### Default values\nFields can specify default values inline using `= value` syntax:\n\n```protobuf\nmessage Config {\n    string name = \"default\";\n    int count = 42;\n    double ratio = 3.14;\n    bool enabled = true;\n}\n```\n\n### `map\u003cK,V\u003e` fields\nMap-type fields are supported using the `map\u003cKeyType, ValueType\u003e` syntax:\n\n```protobuf\nmessage Config {\n    map\u003cstring, string\u003e settings;\n    map\u003cstring, int32\u003e counts;\n}\n```\n\n### XML reference embedding (`@Embedded`)\nWhen serializing data classes to XML, all data fields and references are normally represented by XML tags with the same \nname as the field or reference. By adding the `@Embedded` annotation to a reference, the tag for the reference can be \nomitted. The contents of the reference is placed directly within the tag for the containing element. Care must be taken \nthat the tag names for referenced elements do not clash with tag names of other attributes and references of the \ncontainer.\n\nIn the following example, a container with contents A and B can be written `\u003ccontainer\u003e\u003ca/\u003e\u003cb/\u003e\u003c/container\u003e` instead of \nwrapping the contents into an extra element as in `\u003ccontainer\u003e\u003ccontents\u003e\u003ca/\u003e\u003cb/\u003e\u003c/contents\u003e\u003c/container\u003e`. \n \n```protobuf\nmessage Container {\n    @Embedded\n    repeated Base contents;\n}\n\nabstract message Base {}\nmessage A extends Base {}\nmessage B extends Base {}\n```\n\nHowever, even with the `@Embedded` annotation, the verbose serialization with the wrapping reference element is also \nunderstood.\n\n## Plugin options\n\n### `option DartLib = \"path/to/output.dart\";`\nGenerates a Dart library file containing Dart data classes with JSON serialization support for all messages and enums\ndefined in the proto file. The path is relative to the generator output directory.\n\n```protobuf\noption DartLib = \"../lib/protocol.dart\";\n\nmessage MyMessage {\n    string name;\n    int count;\n}\n```\n\n## Installation in Eclipse\n\nThere is an Eclipse plugin providing a project builder that automatically generates corresponding Java files whenever you create or modify a `*.proto` definition file. To install and enable the plugin with the following steps:\n\n### Add update site\n\n * Open the dialog `Help \u003e Install new Software`.\n * Enter `msgbuf - https://msgbuf.github.io/msgbuf/update-site/` in the `Work with` field, click the `Add...` button, and acknowledge the addition. \n * Select the `MsgBuf Project Builder` checkbox and click `Finish`. \n * Accept the license and the installation of unsigned content.\n\n### Enable the MsgBuf Builder in your project\n\n * Select your project in the `Package Explorer`.\n * In the context menu, select `Configure \u003e Enable MsgBuf Builder`.\n\n### Test the installation\n\n * Create a `MyMessage.proto` file in one of your packages in the source folder.\n * Add the package definition and a message declaration.\n * Immediately, when you save your changes, a corresponding `MyMessage` class should appear that can be directly used in your code.\n\n## Features\n\n### Polymorphic JSON serialization\n\nAll of the generated data classes have get- and set-methods for their properties. Additionally, each class has methods for writing its contents to JSON format and reading it back:\n\n```java\n/** Reads a new instance from the given reader. */\npublic static Rectangle readRectangle(JsonReader in) throws IOException {\n   ...\n}\n\n/** Writes this instance to the given output. */\npublic final void writeTo(JsonWriter out) throws IOException {\n   ...\n}\n```\n\nNote that `JsonReader` and `JsonWriter` use `de.haumacher.msgbuf.io.Reader` and `de.haumacher.msgbuf.io.Writer` instead of `java.io.Reader` and `java.io.Writer`. This is required for GWT compatibility, since `java.io` is not available in GWT. For in-memory string-based serialization, use `StringR` and `StringW`:\n\n```java\n// Writing to a JSON string:\nStringW out = new StringW();\nrectangle.writeTo(new JsonWriter(out));\nString json = out.toString();\n\n// Reading from a JSON string:\nRectangle copy = Rectangle.readRectangle(new JsonReader(new StringR(json)));\n```\n\nOn the server side (where GWT compatibility is not needed), you can use `ReaderAdapter` and `WriterAdapter` from `de.haumacher.msgbuf.server.io` to wrap standard `java.io.Reader`/`java.io.Writer` instances:\n\n```java\n// Reading from a java.io.Reader:\nRectangle r = Rectangle.readRectangle(new JsonReader(new ReaderAdapter(javaIoReader)));\n\n// Writing to a java.io.Writer:\nrectangle.writeTo(new JsonWriter(new WriterAdapter(javaIoWriter)));\n```\n\nIn polymorphic hierarchy of classes as defined above, it is not enough for a class to just write its own properties. Consider a `Group` instance from the example above. Its `shapes` list may contain multiple instances of either circles, rectangles, or even nested groups. Therefore, a class in a polymorphic hierarchy not only serializes its properties, but also its type. Reading back such polymorphic instance instantiates the correct class and fills it with its properties.\n\n### Visitor pattern\n\nIn a client-server messaging scenario, the server (or client) receives polymorphic messages and must dispatch each one to the appropriate handler. Consider a protocol with different request types:\n\n```protobuf\nabstract message Request {\n    string sessionId;\n}\n\nmessage LoginRequest extends Request {\n    string username;\n    string password;\n}\n\nmessage QueryRequest extends Request {\n    string query;\n    int32 limit;\n}\n\nmessage LogoutRequest extends Request {\n}\n```\n\nYou could use `instanceof` checks to handle each request type, but this is fragile — if a new request type is added to the protocol, the compiler won't warn you about the missing case, leading to silent failures at runtime.\n\nThe generated visitor pattern solves this. Each abstract message hierarchy generates a `Visitor` interface with a case for every concrete subtype. When a new message type is added, every visitor implementation fails to compile until the new case is handled, guaranteeing completeness at compile time.\n\n```java\npublic class RequestHandler implements Request.Visitor\u003cResponse, Session\u003e {\n    @Override\n    public Response visit(LoginRequest self, Session session) {\n        return authenticate(self.getUsername(), self.getPassword());\n    }\n\n    @Override\n    public Response visit(QueryRequest self, Session session) {\n        return executeQuery(self.getQuery(), self.getLimit());\n    }\n\n    @Override\n    public Response visit(LogoutRequest self, Session session) {\n        session.invalidate();\n        return Response.ok();\n    }\n}\n\n// Dispatching a received message:\nRequest request = Request.readRequest(new JsonReader(input));\nResponse response = request.visit(handler, session);\n```\n\nThe visitor pattern also works for non-messaging use cases. For the shape hierarchy defined above, a renderer could be implemented as follows.\n\nAn `abstract` base class provides a `Visitor` interface and a `visit(...)` method accepting such a visitor:\n\n```java\npublic abstract class Shape {\n\n   /** Visitor interface for the {@link Shape} hierarchy.*/\n   public interface Visitor\u003cR,A\u003e {\n\n      /** Visit case for {@link Circle}.*/\n      R visit(Circle self, A arg);\n\n     /** Visit case for {@link Rectangle}.*/\n     R visit(Rectangle self, A arg);\n\n     /** Visit case for {@link Group}.*/\n     R visit(Group self, A arg);\n\n   }\n  \n   ...\n\n   /** Accepts the given visitor. */\n   public abstract \u003cR,A\u003e R visit(Visitor\u003cR,A\u003e v, A arg);\n}\n```\n\nEach of the concrete sub-classes implement the `abstract` visit-method by delegating to the corresponding case-method from the `Visitor` interface:\n\n```java\npublic class Rectangle extends Shape {\n\n   ...\n\n   @Override\n   public \u003cR,A\u003e R visit(Shape.Visitor\u003cR,A\u003e v, A arg) {\n      return v.visit(this, arg);\n   }\n}\n```\n\nThis allows creating e.g. a renderer implementation that handles all concrete types from the shape hierarchy:\n\n```java\npublic class ShapeRenderer implements Shape.Visitor\u003cVoid, Graphics2D\u003e {\n   @Override\n   public Void visit(Rectangle self, Graphics2D g2d) {\n      g2d.drawRect(self.getXCoordinate(), self.getYCoordinate(), self.getWidth(), self.getHeight());\n      return null;\n   }\n\n   @Override\n   public Void visit(Circle self, Graphics2D g2d) {\n      ...\n   }\n\n   @Override\n   public Void visit(Group self, Graphics2D g2d) {\n      for (Shape shape : self.getShapes()) {\n         shape.visit(this, g2d);\n      }\n      return null;\n   }\n}\n```\n\nHaving an arbitrary `Shape` instance and a renderer from above, you can render the shape to a `Graphics2D` with the following code:\n\n```java\nShape shape = ...;\nShapeRenderer renderer = ...;\nGraphics2D g2d = ...;\n\nshape.visit(renderer, g2d);\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmsgbuf%2Fmsgbuf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmsgbuf%2Fmsgbuf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmsgbuf%2Fmsgbuf/lists"}