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 031/** Plain-text renderer for {@link SysInfo} audit records. */ 032public final class SysInfoTxtLogRenderer implements LogRenderer<SysInfo> { 033 /** Default constructor; no instance state to initialise. */ 034 public SysInfoTxtLogRenderer() {} 035 public Class<?> clazz() { 036 return SysInfo.class; 037 } 038 public Type type() { 039 return Type.TXT; 040 } 041 042 @Override 043 public void render(SysInfo info, PrintStream ps, String indent) { 044 final String pre = (indent == null) ? "" : indent; 045 /* 046 * 1) Collect all record components in declaration order 047 * together with the effective (JSON) property name 048 * and the value obtained from the accessor. 049 */ 050 Map<String, Object> data = new LinkedHashMap<>(); 051 int maxKeyLen = 0; 052 053 for (RecordComponent rc : SysInfo.class.getRecordComponents()) { 054 String key = effectiveName(rc); 055 Object val; 056 try { 057 val = rc.getAccessor().invoke(info); 058 } catch (Exception e) { 059 val = "(error " + e.getMessage() + ")"; 060 } 061 data.put(key, val); 062 maxKeyLen = Math.max(maxKeyLen, key.length()); 063 } 064 065 final int width = maxKeyLen; 066 data.forEach((k, v) -> { 067 if (v == null) { // skip nulls 068 return; 069 } 070 if (v instanceof List<?> list) { 071 if (list.isEmpty()) { 072 return; 073 } 074 ps.printf("%s%-" + width + "s :%n", pre, k); 075 list.forEach(o -> ps.println(pre + " • " + o)); 076 } else { 077 ps.printf("%s%-" + width + "s : %s%n", pre, k, v); 078 } 079 }); 080 } 081 082 /** Returns the JSON alias if present, otherwise the plain component name. */ 083 private static String effectiveName(RecordComponent rc) { 084 JsonProperty jp = rc.getAnnotation(JsonProperty.class); 085 return (jp != null && !jp.value().isEmpty()) ? jp.value() : rc.getName(); 086 } 087} 088