📋 Product Owner view

Feature inventory, spec alignment, behavioural gaps  ·  Product Owner, Product Manager

Generated 4 days ago
⚡ Agent trace  ·  13 steps  ·  $0.0756  ·  click to expand
search_claims(query='feature capability user', category='any')search_claims(query='api endpoint route', category='any')search_claims(query='authentication authorization login', category='any')search_claims(query='user', category='any')search_claims(query='feature', category='any')search_claims(query='implement', category='any')get_file_claims(file_path='src/main/java/org/joda/money/Money.java')get_file_claims(file_path='src/main/java/org/joda/money/BigMoney.java')get_file_claims(file_path='src/main/java/org/joda/money/CurrencyUnit.java')get_file_claims(file_path='src/main/java/org/joda/money/format/MoneyFormatter.java')get_file_claims(file_path='src/main/java/org/joda/money/format/MoneyFormatterBuilder.java')get_file_claims(file_path='src/main/java/org/joda/money/format/MoneyAmountStyle.java')search_claims(query='README spec documentation', category='any')search_claims(query='test', category='any')get_file_claims(file_path='src/main/java/org/joda/money/format/AmountPrinterParser.java')get_file_claims(file_path='src/main/java/org/joda/money/format/MoneyPrinter.java')get_file_claims(file_path='src/main/java/org/joda/money/format/MoneyParser.java')get_file_claims(file_path='src/main/java/org/joda/money/format/MoneyPrintContext.java')get_file_claims(file_path='src/main/java/org/joda/money/format/MoneyParseContext.java')get_file_claims(file_path='src/main/java/org/joda/money/format/LiteralPrinterParser.java')get_file_claims(file_path='src/main/java/org/joda/money/format/SignedPrinterParser.java')get_file_claims(file_path='src/main/java/org/joda/money/format/MultiPrinterParser.java')get_file_claims(file_path='src/main/java/org/joda/money/format/MoneyFormatException.java')get_file_claims(file_path='src/main/java/org/joda/money/format/GroupingStyle.java')get_file_claims(file_path='src/main/java/org/joda/money/Ser.java')get_file_claims(file_path='src/main/java/org/joda/money/CurrencyUnitDataProvider.java')get_file_claims(file_path='src/main/java/org/joda/money/DefaultCurrencyUnitDataProvider.java')get_file_claims(file_path='src/test/java/org/joda/money/format/TestMoneyFormatter.java')get_file_claims(file_path='src/main/java/org/joda/money/BigMoneyProvider.java')get_file_claims(file_path='src/main/java/org/joda/money/IllegalCurrencyException.java')get_file_claims(file_path='src/main/java/org/joda/money/CurrencyMismatchException.java')get_file_claims(file_path='src/test/java/org/joda/money/TestMoney.java')get_file_claims(file_path='src/test/java/org/joda/money/TestCurrencyUnit.java')search_claims(query='readme spec doc', category='any')get_file_claims(file_path='src/main/java/org/joda/money/format/MoneyFormatterBuilder.java')read_source_file(file_path='src/main/java/org/joda/money/Money.java', start_line=226)read_source_file(file_path='src/main/java/org/joda/money/BigMoney.java', start_line=247)read_source_file(file_path='src/main/java/org/joda/money/format/MoneyFormatterBuilder.java', start_line=85)

Product View: Joda-Money Library

Feature Inventory

Feature Status Confidence Notes
Monetary Value Representation (Money) Implemented High Fixed-scale money class (scale = currency's decimal places). Final, immutable, thread-safe. Money.java:45-1350
Arbitrary-Precision Money (BigMoney) Implemented High Variable-scale money class. Immutable, thread-safe. BigMoney.java:45-1736
Currency Unit Registry Implemented High ISO-4217 currency codes, numeric codes, decimal places, country mappings. Loaded from CSV data files. CurrencyUnit.java:50-677
Currency Data Provider (Extensible) Implemented High Abstract CurrencyUnitDataProvider allows custom currency registration. Default loads from classpath CSV. CurrencyUnitDataProvider.java:21-58, DefaultCurrencyUnitDataProvider.java:38-118
Currency Lookup (by code, numeric code, locale, country) Implemented High of(String), ofNumericCode(String/int), of(Locale), ofCountry(String). CurrencyUnit.java:415-422
Currency Constants (USD, EUR, JPY, GBP, CHF, AUD, CAD) Implemented High Seven static final constants for major currencies. CurrencyUnit.java:415-422
Money Arithmetic (add, subtract, multiply, divide) Implemented High Full set of operations on both Money and BigMoney. Scale-aware. Money.java:390-395, BigMoney.java:904-910
Money Comparison (compareTo, isEqual, isGreaterThan, isLessThan) Implemented High Comparison ignores scale (like BigDecimal). Throws CurrencyMismatchException if currencies differ. Money.java:1223-1242, BigMoney.java:1608-1615
Currency Conversion Implemented High convertedTo(CurrencyUnit, BigDecimal, RoundingMode) with validation (rejects negative rates, same-currency with non-1 factor). Money.java:1184-1186, BigMoney.java:1516-1530
Money Parsing (from string) Implemented High Money.parse("GBP 2.43") and BigMoney.parse(). Format: CCC amount. Money.java:226-230, BigMoney.java:262-267
Money Formatting (builder pattern) Implemented High MoneyFormatterBuilder with appendAmount(), appendCurrencyCode(), appendLiteral(), etc. MoneyFormatterBuilder.java:399-402
Amount Formatting Styles Implemented High 8 predefined styles (ASCII, localized, grouping variants). Customizable grouping, decimal point, zero character, sign. MoneyAmountStyle.java:50-680
Localized Currency Symbol Partial High Can print localized symbols (e.g., £, $) but cannot parse them — parser is null. MoneyFormatterBuilder.java:143-145
Signed Formatting (positive/zero/negative variants) Implemented High appendSigned() allows different formatters for positive, zero, and negative amounts. MoneyFormatterBuilder.java:190-192
Serialization Implemented High Custom Externalizable implementation with validation on deserialization (rejects tampered data). Ser.java:33-159
Pseudo-Currency Support Implemented High Currencies with decimalPlaces < 0 (e.g., XXX, XFU). isPseudoCurrency() returns true. CurrencyUnit.java:560-562
Custom Currency Registration (programmatic) Implemented High registerCurrency() with force flag to override existing entries. CurrencyUnit.java:275-283
Country-to-Currency Mapping Implemented High ofCountry(String) and registerCountry(). CurrencyUnit.java:299-301
Thread Safety Guarantees Implemented High All core classes are immutable and thread-safe. MoneyPrinter/MoneyParser interfaces require thread-safe implementations. MoneyPrinter.java:35-37, MoneyParser.java:42-44

What the Code Does That the Spec Doesn't Mention

Since no formal spec or README was found in the extracted claims, the following are undocumented product decisions discovered in the code:

  1. Android workaround for stripTrailingZeros() bugBigMoney.of(CurrencyUnit, double) returns zero for a zero double value to avoid a bug in stripTrailingZeros() on Android before v30. This is a platform-specific fix with no documentation. BigMoney.java:262-267

  2. Money.equals() vs compareTo() inconsistencyequals() considers scale (so "USD 30.00" ≠ "USD 30"), while compareTo() ignores scale. This is documented in Javadoc but is a subtle behavioral trap for users. Money.java:1314-1323, Money.java:1223-1226

  3. CurrencyMismatchException allows null first currency — The constructor allows firstCurrency to be null but requires secondCurrency to be non-null per Javadoc. This asymmetry is undocumented from a product perspective. CurrencyMismatchException.java:42-50

  4. GroupingStyle.FULL has a copy-paste documentation error — The Javadoc for FULL says "No grouping occurs" (same as NONE), which is clearly wrong. The actual behavior is full grouping. GroupingStyle.java:35-36

  5. DefaultCurrencyUnitDataProvider silently ignores malformed CSV lines — Lines that don't match the expected regex patterns are silently skipped with no warning or error. DefaultCurrencyUnitDataProvider.java:94-104

  6. DefaultCurrencyUnitDataProvider Javadoc has a typo in file name — The docstring mentions 'CurencyData.csv' but the code uses 'CurrencyData.csv'. DefaultCurrencyUnitDataProvider.java:28-36

  7. MoneyFormatterBuilder.appendCurrencySymbolLocalized() is print-only — The localized currency symbol can be printed but cannot be parsed back. This is documented in the Javadoc but is a significant limitation for round-trip formatting. MoneyFormatterBuilder.java:143-145

  8. MoneyFormatter catches and wraps IOException — The print(Appendable, BigMoneyProvider) method catches IOException and wraps it in MoneyFormatException. The rethrowIOException() method allows callers to recover the original IOException. MoneyFormatter.java:183-189, MoneyFormatException.java:68-72

  9. BigMoneyProvider interface explicitly warns about mutability — The interface documentation warns that implementations may be mutable and callers should convert to BigMoney immediately. This is a design decision that allows flexibility but shifts responsibility to the caller. BigMoneyProvider.java:39-43

  10. Money constructor uses AssertionError for validation — The private constructor throws AssertionError (not IllegalArgumentException) if the underlying BigMoney has the wrong scale. This is an internal guard, not a public API contract. TestMoney.java:791-815


Spec Gaps

No formal specification document (README, spec, or requirements) was found in the extracted claims. However, based on what a reasonable user would expect from a money library:

  1. No rounding mode on Money.of(CurrencyUnit, BigDecimal) — The basic factory method throws ArithmeticException if the amount has more decimal places than the currency supports. Users must explicitly use the of(CurrencyUnit, BigDecimal, RoundingMode) overload to handle rounding. This is a deliberate design choice (fail-fast) but could surprise users. Money.java:226-230

  2. Money.total() with empty input throws IllegalArgumentException — Calling total() with an empty array or iterable throws an exception rather than returning zero. This is a design decision that may not match user expectations. Money.java:245-256

  3. BigMoney.equals() considers scale — Unlike BigDecimal, where equals() considers scale but compareTo() doesn't, BigMoney follows the same pattern. This means "USD 30.00" and "USD 30" are not equal, which could be surprising. BigMoney.java:1695-1705

  4. Currency conversion with same currency and non-1 factor throwsconvertedTo() throws IllegalArgumentException if the target currency is the same as the source and the conversion multiplier is not 1. This prevents accidental no-op conversions but may not be obvious. BigMoney.java:1516-1530

  5. No built-in support for currency exchange rate providers — The library provides convertedTo() but has no mechanism for plugging in exchange rate services. Users must supply the conversion rate themselves.


Open Questions for Engineering

  1. What is the intended use case for BigMoneyProvider? — The interface explicitly warns that implementations may be mutable. Is there a planned mutable money type, or is this just future-proofing? This affects how we document the API to customers.

  2. Why does GroupingStyle.FULL have the wrong Javadoc? — The docstring says "No grouping occurs" which is clearly a copy-paste error from NONE. This should be fixed in the next release to avoid confusion.

  3. Is the Android stripTrailingZeros() workaround still needed? — The workaround targets Android versions before v30. Should we document this or consider it a legacy concern?

  4. What is the CSV file name discrepancy about? — The Javadoc mentions 'CurencyData.csv' (typo) but the code uses 'CurrencyData.csv'. Is this a documentation bug, or is there a historical reason for the discrepancy?

  5. Should Money.total() with empty input return zero instead of throwing? — This is a common point of friction. Consider whether a totalOrZero() method or a behavioral change would better serve users.

  6. Is the equals() vs compareTo() scale behavior intentional for Money?Money delegates to BigMoney for equals(), which considers scale. But Money is supposed to always be at currency scale. Can two Money instances with the same currency and numeric value ever have different scales? If not, the scale concern in equals() is moot for Money but still relevant for BigMoney.

  7. Should we add a parseCurrencySymbol() capability? — Currently, localized currency symbols can be printed but not parsed. This limits round-trip formatting for internationalized applications. Is this a feature we should prioritize?