001/*
002 * jPOS Project [http://jpos.org]
003 * Copyright (C) 2000-2026 jPOS Software SRL
004 *
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017 */
018
019package org.jpos.log.render.txt;
020
021import com.fasterxml.jackson.annotation.JsonProperty;
022import org.jpos.log.LogRenderer;
023import org.jpos.log.evt.SysInfo;
024
025import java.io.PrintStream;
026import java.lang.reflect.RecordComponent;
027import java.util.LinkedHashMap;
028import java.util.List;
029import java.util.Map;
030
031public final class SysInfoTxtLogRenderer implements LogRenderer<SysInfo> {
032    public Class<?> clazz() {
033        return SysInfo.class;
034    }
035    public Type type() {
036        return Type.TXT;
037    }
038
039    @Override
040    public void render(SysInfo info, PrintStream ps, String indent) {
041        final String pre = (indent == null) ? "" : indent;
042        /*
043         * 1) Collect all record components in declaration order
044         *    together with the effective (JSON) property name
045         *    and the value obtained from the accessor.
046         */
047        Map<String, Object> data = new LinkedHashMap<>();
048        int maxKeyLen = 0;
049
050        for (RecordComponent rc : SysInfo.class.getRecordComponents()) {
051            String key = effectiveName(rc);
052            Object val;
053            try {
054                val = rc.getAccessor().invoke(info);
055            } catch (Exception e) {
056                val = "(error " + e.getMessage() + ")";
057            }
058            data.put(key, val);
059            maxKeyLen = Math.max(maxKeyLen, key.length());
060        }
061
062        final int width = maxKeyLen;
063        data.forEach((k, v) -> {
064            if (v == null) {                         // skip nulls
065                return;
066            }
067            if (v instanceof List<?> list) {
068                if (list.isEmpty()) {
069                    return;
070                }
071                ps.printf("%s%-" + width + "s :%n", pre, k);
072                list.forEach(o -> ps.println(pre + "  • " + o));
073            } else {
074                ps.printf("%s%-" + width + "s : %s%n", pre, k, v);
075            }
076        });
077    }
078
079    /** Returns the JSON alias if present, otherwise the plain component name. */
080    private static String effectiveName(RecordComponent rc) {
081        JsonProperty jp = rc.getAnnotation(JsonProperty.class);
082        return (jp != null && !jp.value().isEmpty()) ? jp.value() : rc.getName();
083    }
084}
085