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.util;
020
021import java.io.ByteArrayOutputStream;
022import java.io.IOException;
023import java.io.OutputStream;
024
025import org.jpos.iso.ISOUtil;
026
027import java.io.PrintStream;
028import java.util.Arrays;
029import java.util.Collection;
030
031/**
032 * <p>
033 * A simple general purpose loggeable message.
034 * </p>
035 *
036 * @author Hani S. Kirollos
037 * @version $Revision$ $Date$
038 */
039public class SimpleMsg implements Loggeable {
040
041    static final String STACKTRACE_EXTRA_INDENT = "    ";
042
043    static final String STACKTRACE_TAB_REPLACE = STACKTRACE_EXTRA_INDENT + STACKTRACE_EXTRA_INDENT;
044
045    String tagName;
046    String msgName;
047    Object msgContent;
048
049    /**
050     * Constructs a SimpleMsg with an XML tag, an optional name attribute, and a payload.
051     *
052     * @param tagName XML tag name
053     * @param msgName optional name attribute, or {@code null}
054     * @param msgContent message payload (rendered specially for byte arrays, collections, throwables, and loggeables)
055     */
056    public SimpleMsg(String tagName, String msgName, Object msgContent) {
057        this.tagName = tagName;
058        this.msgName = msgName;
059        if (msgContent instanceof byte[])
060            this.msgContent = ISOUtil.hexString((byte[]) msgContent);
061        else
062            this.msgContent = msgContent;
063    }
064
065    /**
066     * Constructs a SimpleMsg with no name attribute.
067     *
068     * @param tagName XML tag name
069     * @param msgContent message payload
070     */
071    public SimpleMsg(String tagName, Object msgContent) {
072        this(tagName, null, msgContent);
073    }
074
075    /**
076     * dumps message
077     *
078     * @param p      a PrintStream usually supplied by Logger
079     * @param indent indention string, usually suppiled by Logger
080     * @see org.jpos.util.Loggeable
081     */
082    @Override
083    public void dump(PrintStream p, String indent) {
084        String inner = indent + "  ";
085        p.print(indent + "<" + tagName);
086        if (msgName != null)
087            p.print(" name=\"" + msgName + "\"");
088
089        Collection cl = null;
090        if (msgContent instanceof Object[])
091            cl = Arrays.asList((Object[]) msgContent);
092        else if (msgContent instanceof Collection)
093            cl = (Collection) msgContent;
094        else if (msgContent instanceof Loggeable)
095            cl = Arrays.asList(msgContent);
096        else if (msgContent instanceof Throwable)
097            cl = Arrays.asList(msgContent);
098        else if (msgName != null && msgContent == null) {
099            p.println("/>");
100            return;
101        } else if (msgName != null)
102            cl = Arrays.asList(msgContent);
103        else if (msgContent != null)
104            p.print(">" + msgContent);
105        else {
106            p.println("/>");
107            return;
108        }
109
110        if (cl != null) {
111            p.println(">");
112            for (Object o : cl) {
113                if (o instanceof Loggeable)
114                    ((Loggeable) o).dump(p, inner);
115                else if (o instanceof Throwable)
116                    p.print(formatThrowable(indent, (Throwable) o));
117                else
118                    p.println(inner + o);
119            }
120            p.print(indent);
121        }
122
123        p.println("</" + tagName + ">");
124    }
125
126    private String formatThrowable(String indent, Throwable t) {
127        String inde = indent + STACKTRACE_EXTRA_INDENT;
128        try (
129                OutputStream os = new ByteArrayOutputStream();
130                PrintStream ps = new PrintStream(os);
131        ) {
132            // indent stack trace first line
133            ps.print(inde);
134            t.printStackTrace(ps);
135
136            String res = os.toString();
137            res = res.replace("\n\t", "\n" + inde + STACKTRACE_TAB_REPLACE);
138            res = res.replace("\nCaused by:", "\n" + inde + "Caused by:");
139            return res;
140        } catch (IOException ex) {
141            return ""; // it should never occur for ByteArrayOutputStream
142        }
143    }
144
145    /**
146     * Replaces the message payload.
147     *
148     * @param msgContent new payload
149     */
150    public void setMsgContent(Object msgContent) {
151        this.msgContent = msgContent;
152    }
153
154    /**
155     * Returns the current message payload.
156     *
157     * @return the payload (may be {@code null})
158     */
159    public Object getMsgContent() {
160        return msgContent;
161    }
162}