Skip to content

Conversation

@haumacher
Copy link

To use the library in Java server applications, it would be wonderful to have a Java version of the code. It would be even more useful, if the library could be imported as dependency from Maven Central.

Besides translation, I added an attribution to the generated SVG images according to the LICENSE requirements.

I intentionally added the Java version to the same repo to easy synching changes, but I also could move the Java version to a separate library under the "home" of Multiavatar.

If you whish, I could help preparing the library for deploying to Maven central. I used the Maven coordinates

    <groupId>com.multiavatar</groupId>
    <artifactId>multiavatar</artifactId>
    <version>1.0.7</version>

haumacher and others added 30 commits January 11, 2026 16:48
…cement behavior

This commit achieves 100% cross-platform compatibility between the Java
and JavaScript implementations (all 25 test vectors now pass).

Key changes:

1. Add missing character 15 (Street) SVG data and themes
   - Character 15 was missing from SvgData.java, causing failures for
     test cases that used characters with hash values mapping to 15
   - Added all 6 SVG parts (env, clo, head, mouth, eyes, top) for
     character 15 from the JavaScript source

2. Fix color replacement algorithm to match JavaScript behavior
   - JavaScript uses String.replace() in a loop, which has a quirk:
     when you replace A→B, then later B→C, it may re-replace the newly
     created B value (cascading replacements)
   - Java was using Matcher.appendReplacement() which processes matches
     strictly left-to-right without this cascading behavior
   - Changed to use replaceFirst() in a loop to exactly match JavaScript
   - This "bug-for-bug" compatibility is necessary for identical output

Test results:
- Before: 18/25 tests passing (72%)
- After: 25/25 tests passing (100%)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
The tests were expecting valid SVG output for empty/null inputs, but
the JavaScript implementation returns an empty string in these cases.
Updated assertions to match the actual (and correct) behavior:
- Empty string input returns empty string
- Null input returns empty string

This is verified by test-vectors.json case 6 which confirms JS returns
empty string for empty input.

All tests now pass (12/12).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This file was a placeholder that is no longer needed. All SVG data
is now stored in SvgData.java as Java code, not loaded from JSON.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Changed from git:// and ssh:// protocols to https:// for better
compatibility and security. The git:// protocol is deprecated and
blocked by many firewalls.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Added build resources configuration to include the LICENSE file in
the JAR artifact. This is important because Multiavatar uses a
custom license (MULTIAVATAR LICENSE v1.0) with specific restrictions
that users need to be aware of when using the library.

The LICENSE file is now packaged at META-INF/LICENSE in the JAR.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Added GenerateExamplesTest that creates 33 example avatar SVG files
in target/examples/ for manual inspection:
- 20 avatars from various input strings (names, emails, etc.)
- 4 avatars without background (sansEnv=true)
- 9 avatars with forced versions (3 characters × 3 themes)

Run with: mvn test -Dtest=GenerateExamplesTest

The generated SVG files can be opened in any web browser to view
the avatars and verify the visual output.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Modified the Java implementation to include proper SVG metadata with
Dublin Core properties for attribution. This fulfills the attribution
requirement specified in the MULTIAVATAR LICENSE v1.0.

Changes:
- Multiavatar.java: Added <metadata> element with dc:creator and
  dc:source after opening <svg> tag
- CrossPlatformCompatibilityTest: Strip metadata before comparing
  with JavaScript output (intentional difference for license compliance)
- MultiavatarTest: Added assertions to verify metadata is present

The metadata format follows SVG best practices:
  <metadata>
    <dc:creator>Multiavatar</dc:creator>
    <dc:source>https://multiavatar.com</dc:source>
  </metadata>

This provides proper semantic attribution rather than just a comment,
making it easier for users to comply with license terms while following
SVG standards.

All tests pass (13/13).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Moved the xmlns:dc namespace declaration from the root <svg> element
to the <metadata> element itself, keeping the namespace declaration
local to where it's used. This is cleaner and doesn't affect the rest
of the SVG structure.

Generated SVGs now include:
  <metadata xmlns:dc="http://purl.org/dc/elements/1.1/">
    <dc:creator>Multiavatar</dc:creator>
    <dc:source>https://multiavatar.com</dc:source>
  </metadata>

All tests pass (13/13).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Created an enum to represent the six avatar parts (ENV, HEAD, CLO,
TOP, EYES, MOUTH) in their rendering order. This provides:
- Type safety when iterating over parts
- Clear documentation of the rendering order
- Better maintainability vs hardcoded string arrays

The enum maintains the original string values for compatibility with
existing methods like getFinalSvg() and getValue().

All tests pass (13/13).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Refactored all internal methods to use AvatarPart enum instead of
string-based part names:
- Parts.getValue() now accepts AvatarPart instead of String
- getFinalSvg() now accepts AvatarPart instead of String
- getColorsForPart() now accepts AvatarPart instead of String
- Main loop no longer converts enum to string

Strings are now only used at the boundary with SvgData.getSvgPart()
which still uses strings for lookups. This eliminates all the
string-based switch statements and provides full type safety throughout
the avatar generation process.

All tests pass (13/13).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Completed the enum refactoring by updating SvgData:
- Changed internal storage from Map<String, Map<String, String>>
  to Map<String, EnumMap<AvatarPart, String>>
- Updated all 16 character definitions to use enum constants
  (ENV, CLO, HEAD, MOUTH, EYES, TOP) instead of strings
- Changed getSvgPart() to accept AvatarPart instead of String
- Made AvatarPart package-private so SvgData can access it
- Updated DebugTest to use the new enum-based API

Benefits:
- Complete elimination of string-based part names throughout codebase
- Type safety at compile time - impossible to use invalid part names
- EnumMap provides better performance than HashMap for enum keys
- Single source of truth for part names in the AvatarPart enum

All tests pass (13/13).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Created AvatarCharacter enum with all 16 character types:
ROBO, GIRL, BLONDE, GUY, COUNTRY, GEEKNOT, ASIAN, PUNK, AFROHAIR,
NORMIE_FEMALE, OLDER, FIREHAIR, BLOND, ATEAM, RASTA, STREET

Changes:
- SvgData now uses EnumMap<AvatarCharacter, EnumMap<AvatarPart, String>>
  instead of Map<String, EnumMap<AvatarPart, String>>
- All 16 character definitions use enum constants instead of "00"-"15"
- getSvgPart() accepts AvatarCharacter instead of String
- Added fromId() and fromIndex() helper methods for conversion
- Updated DebugTest to use the new enum-based API

Benefits:
- No more magic strings like "00", "05", "15"
- Type-safe character selection
- Self-documenting code with meaningful names (ROBO vs "00")
- EnumMap provides better performance than HashMap
- Impossible to reference non-existent characters at compile time

All tests pass (13/13).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Updated ThemeData to use the AvatarCharacter enum:
- Changed internal storage from Map<String, CharacterThemes>
  to EnumMap<AvatarCharacter, CharacterThemes>
- Updated all 16 character theme definitions to use enum constants
  instead of "00"-"15" strings
- Changed getCharacterThemes() to accept AvatarCharacter instead of String
- Updated test files to use the enum-based API

Benefits:
- Complete elimination of character ID strings throughout the codebase
- Type safety when accessing theme data
- EnumMap provides better performance than HashMap
- Self-documenting code (GEEKNOT vs "05")
- Compile-time verification of valid character references

The only remaining string usage is the partId variable in getFinalSvg(),
which is converted to AvatarCharacter enum immediately. This string comes
from the hash-based part selection algorithm and is the boundary between
the hash calculation and the type-safe enum system.

All tests pass (13/13).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Compensate fix of absurd expansion strategy with fixed theme data
values.
Replace string-based encoding of part ID and theme (e.g., "05A") with
explicit PartWithTheme value class that holds AvatarCharacter and Theme.

Changes:
- Add Theme enum (A, B, C) and PartWithTheme value class
- Update Parts helper class to store PartWithTheme instead of strings
- Update Version class to use AvatarCharacter instead of string part ID
- Remove getPartWithTheme() method and inline logic into PartWithTheme
- Update getFinalSvg() to accept AvatarCharacter directly
- Remove deprecated string-based Version constructor
- Update all tests and documentation to use new API

Benefits:
- Type safety: Using enums prevents invalid values
- Clearer code: Explicit structure instead of string encoding/decoding
- No more lookups: Eliminated AvatarCharacter.fromId() calls
- Better semantics: Code directly expresses domain concepts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Refactor CharacterThemes to use Multiavatar.Theme enum internally instead
of having redundant A, B, C fields and char-based lookup.

Changes:
- CharacterThemes now uses EnumMap<Multiavatar.Theme, Theme> internally
- Update getTheme() to accept Multiavatar.Theme enum instead of char
- Update Version class to use Theme enum instead of char
- Update getFinalSvg() to accept Theme enum instead of char
- Update all test files to use Theme enum
- Update documentation to use Theme enum

Benefits:
- Eliminates redundancy between separate A/B/C fields and Theme enum
- Type safety: Using enum prevents invalid theme values
- Cleaner API: No more char-based lookups
- Single source of truth for theme variants

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
The Version class was only used to pass parameters to generate(). Replace
it with direct parameters for a cleaner API.

Changes:
- Add generate(String, boolean, AvatarCharacter, Theme) overload
- Remove Version class entirely
- Update all test files to use direct parameters
- Update documentation to use direct parameters

Benefits:
- Simpler API: No need to create wrapper objects
- Fewer classes: Removed unnecessary Version class
- Clearer intent: Parameters directly show what's being forced
- Less boilerplate: Direct parameter passing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
The previous API mixed two concerns: generating an avatar from a string ID
versus using a predefined character/theme. Split into two separate methods
with shared rendering logic.

Changes:
- Add generate(CharacterType, CharacterTheme) for predefined avatars
- Add generate(CharacterType, CharacterTheme, boolean) with sansEnv option
- Keep generate(String) and generate(String, boolean) for ID-based generation
- Extract shared renderAvatar(Avatar, boolean) method to avoid duplication
- Add Avatar.fromCharacterTheme() factory method
- Remove confusing generate(String, boolean, CharacterType, CharacterTheme)
- Update all tests to use appropriate API
- Update documentation

Benefits:
- Clear separation of concerns: ID-based vs predefined avatars
- No more confusing mixed parameters
- Cleaner API: each method has a single, clear purpose
- No code duplication: shared rendering logic

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
The render method operates on Avatar data, so it should be an instance
method. Also rename the confusing 'string' parameter to 'id' to clarify
that it's an identifier for the avatar.

Changes:
- Move renderAvatar() from static method to Avatar.render() instance method
- Update generate() methods to call avatar.render()
- Rename 'string' parameter to 'id' throughout
- Update javadoc to clarify that 'id' can be username, email, etc.

Benefits:
- Better encapsulation: Avatar knows how to render itself
- Clearer naming: 'id' is more descriptive than 'string'
- Better OOP design: behavior is with the data

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Introduce Avatar.fromRandom(Random) and Multiavatar.generate(Random) methods to support generating avatars with random parts using a provided Random instance. This enables reproducible random avatar generation when needed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
haumacher and others added 7 commits January 11, 2026 21:48
- Add JavaDoc {@link} tags for all class references in documentation
- Add complete JavaDoc for Avatar.pure() and Avatar.fromId() methods
- Update Multiavatar class documentation to include all generation modes
- Fix outdated comment references (PartWithTheme → Coordinate)
- Update README-JAVA.md with current API (remove Version class references)
- Add documentation for random avatar generation methods
- Fix character 15 name in README: "Meta" → "Street"
- Update code examples with proper imports and current API usage

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Introduce generate(long seed) and generate(long seed, boolean sansEnv)
methods to support reproducible random avatar generation. The seed is
used to create a new Random instance, ensuring the same seed always
produces the same avatar.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Update test vector generation to include all 48 pure avatar combinations
(16 characters × 3 themes) with and without backgrounds, bringing total
test vectors from 25 to 132. This ensures comprehensive cross-platform
compatibility testing between JavaScript and Java implementations.

Changes:
- Rename generate-test-vectors.js to .cjs for CommonJS compatibility
- Add automatic generation of all pure avatar test cases
- Fix API call to properly handle null/undefined for hash-based generation
- Update test-vectors.json with 96 additional pure avatar test cases
- Add TEST-VECTORS.md documentation explaining how to generate and update
  test vectors

All 132 test vectors pass in CrossPlatformCompatibilityTest.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Document each avatar part constant with its purpose and layer description:
- ENV: Environment/circular background layer
- HEAD: Face/head shape layer
- CLO: Clothing/body layer
- TOP: Hair/headwear layer
- EYES: Eyes layer
- MOUTH: Mouth/expression layer

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@giekaton
Copy link
Member

Hi, thanks for the PR.

The Java version would be better as a separate repository (e.g. multiavatar/multiavatar-java) rather than merged into the main JS repo.

If we go that route, are you willing to fully prepare it (including tests, documentation, and packaging) and take long-term responsibility for maintaining the Java implementation?

I'd keep my role limited to the reference JS version and overall direction.

@haumacher
Copy link
Author

haumacher commented Jan 13, 2026

Yes, I can do that. Of cause, I'll need your help to get access to the corresponding namespace in Maven Central - requires some DNS proof of domain ownership.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants