All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nl.weeaboo.lua2.stdlib.FormatDesc Maven / Gradle / Ivy

package nl.weeaboo.lua2.stdlib;

import java.io.Serializable;
import java.util.Locale;

import nl.weeaboo.lua2.LuaException;
import nl.weeaboo.lua2.io.LuaSerializable;
import nl.weeaboo.lua2.vm.Buffer;
import nl.weeaboo.lua2.vm.LuaString;

@LuaSerializable
final class FormatDesc implements Serializable {

    private static final long serialVersionUID = 1L;
    private static final int MAX_FLAGS = 5;

    private boolean leftAdjust;
    private boolean zeroPad;
    private boolean explicitPlus;
    private boolean space;

    private int width;
    private int precision;

    public final int conversion;
    public final int length;

    public FormatDesc(LuaString strfrmt, final int start) {
        int p = start;
        int n = strfrmt.length();
        int c = 0;

        boolean moreFlags = true;
        while (moreFlags) {
            switch (c = ((p < n) ? strfrmt.luaByte(p++) : 0)) {
            case '-':
                leftAdjust = true;
                break;
            case '+':
                explicitPlus = true;
                break;
            case ' ':
                space = true;
                break;
            case '0':
                zeroPad = true;
                break;
            default:
                moreFlags = false;
                break;
            }
        }
        if (p - start > MAX_FLAGS) {
            throw new LuaException("invalid format (repeated flags)");
        }

        width = -1;
        if (Character.isDigit((char)c)) {
            width = c - '0';
            c = ((p < n) ? strfrmt.luaByte(p++) : 0);
            if (Character.isDigit((char)c)) {
                width = width * 10 + (c - '0');
                c = ((p < n) ? strfrmt.luaByte(p++) : 0);
            }
        }

        precision = -1;
        if (c == '.') {
            c = ((p < n) ? strfrmt.luaByte(p++) : 0);
            if (Character.isDigit((char)c)) {
                precision = c - '0';
                c = ((p < n) ? strfrmt.luaByte(p++) : 0);
                if (Character.isDigit((char)c)) {
                    precision = precision * 10 + (c - '0');
                    c = ((p < n) ? strfrmt.luaByte(p++) : 0);
                }
            }
        }

        if (Character.isDigit((char)c)) {
            throw new LuaException("invalid format (width or precision too long)");
        }

        zeroPad &= !leftAdjust; // '-' overrides '0'
        conversion = c;
        length = p - start;
    }

    public void format(Buffer buf, byte c) {
        // TODO: not clear that any of width, precision, or flags apply here.
        buf.append(c);
    }

    public void format(Buffer buf, long number) {
        String digits;

        if (number == 0 && precision == 0) {
            digits = "";
        } else {
            int radix;
            switch (conversion) {
            case 'x':
            case 'X':
                radix = 16;
                break;
            case 'o':
                radix = 8;
                break;
            default:
                radix = 10;
                break;
            }
            digits = Long.toString(number, radix);
            if (conversion == 'X') {
                digits = digits.toUpperCase(Locale.ROOT);
            }
        }

        int minwidth = digits.length();
        int ndigits = minwidth;
        int nzeros;

        if (number < 0) {
            ndigits--;
        } else if (explicitPlus || space) {
            minwidth++;
        }

        if (precision > ndigits) {
            nzeros = precision - ndigits;
        } else if (precision == -1 && zeroPad && width > minwidth) {
            nzeros = width - minwidth;
        } else {
            nzeros = 0;
        }

        minwidth += nzeros;
        int nspaces = width > minwidth ? width - minwidth : 0;

        if (!leftAdjust) {
            pad(buf, ' ', nspaces);
        }

        if (number < 0) {
            if (nzeros > 0) {
                buf.append((byte)'-');
                digits = digits.substring(1);
            }
        } else if (explicitPlus) {
            buf.append((byte)'+');
        } else if (space) {
            buf.append((byte)' ');
        }

        if (nzeros > 0) {
            pad(buf, '0', nzeros);
        }

        buf.append(digits);

        if (leftAdjust) {
            pad(buf, ' ', nspaces);
        }
    }

    public void format(Buffer buf, double x) {
        StringBuilder format = new StringBuilder("%");
        if (width >= 0) {
            format.append(width);
        }
        if (precision >= 0) {
            format.append('.');
            format.append(precision);
        }
        format.append('f');

        String str = String.format(Locale.ROOT, format.toString(), x);
        buf.append(str);
    }

    public void format(Buffer buf, LuaString str) {
        int nullindex = str.indexOf((byte)'\0', 0);
        if (nullindex >= 0) {
            str = str.substring(0, nullindex);
        }

        // Trim to precision
        str = trimToPrecision(str);

        int pad = Math.max(0, width - str.length());
        if (leftAdjust) {
            buf.append(str);
            pad(buf, ' ', pad);
        } else {
            pad(buf, ' ', pad);
            buf.append(str);
        }
    }

    private LuaString trimToPrecision(LuaString str) {
        if (precision >= 0) {
            return str.substring(0, Math.min(str.length(), precision));
        } else {
            return str;
        }
    }

    public int getPrecision() {
        return precision;
    }

    public static final void pad(Buffer buf, char c, int n) {
        byte b = (byte)c;
        while (n-- > 0) {
            buf.append(b);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy