MainframeAccountLookupAdapter.java

package com.designpatterns.structural.adapter.applied;

/**
 * Exposes the fixed-width, checked-exception {@link MainframeAccountGateway} behind
 * the modern {@link AccountLookupPort} contract new microservices depend on. New code
 * never learns about record offsets or the legacy exception type.
 */
public class MainframeAccountLookupAdapter implements AccountLookupPort {

    private static final int ACCOUNT_NUMBER_WIDTH = 10;
    private static final int HOLDER_NAME_WIDTH = 25;
    private static final int BALANCE_CENTS_WIDTH = 10;

    private final MainframeAccountGateway gateway;

    public MainframeAccountLookupAdapter(MainframeAccountGateway gateway) {
        this.gateway = gateway;
    }

    @Override
    public AccountSnapshot findByAccountNumber(String accountNumber) {
        String padded = zeroPad(accountNumber);
        try {
            return parse(gateway.queryAccountRecord(padded));
        } catch (MainframeUnavailableException e) {
            throw new AccountLookupException("mainframe unavailable for account " + accountNumber, e);
        }
    }

    private AccountSnapshot parse(String record) {
        int nameStart = ACCOUNT_NUMBER_WIDTH;
        int balanceStart = nameStart + HOLDER_NAME_WIDTH;
        int statusIndex = balanceStart + BALANCE_CENTS_WIDTH;

        String accountNumber = record.substring(0, ACCOUNT_NUMBER_WIDTH).trim();
        String holderName = record.substring(nameStart, balanceStart).trim();
        long balanceCents = Long.parseLong(record.substring(balanceStart, statusIndex));
        boolean active = record.charAt(statusIndex) == 'A';

        return new AccountSnapshot(accountNumber, holderName, balanceCents, active);
    }

    private String zeroPad(String accountNumber) {
        return String.format("%" + ACCOUNT_NUMBER_WIDTH + "s", accountNumber).replace(' ', '0');
    }
}