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.function;
020
021import org.jpos.core.Configurable;
022import org.jpos.core.Configuration;
023import org.jpos.core.ConfigurationException;
024
025import java.nio.ByteBuffer;
026
027/**
028 * Mapper for removing newlines from the output of writing LogEvents to a stream.
029 *
030 * Configuration options allow you to optionally combine multiple spaces into a single space and
031 * opting not to add a newline at the end of the data.
032 *
033 * Example: <br>
034 *     <pre>{@code
035 *         <output-mapper class="org.jpos.util.function.RemoveNewLinesMapper">
036 *             <properties name="combine-spaces" value="true"/>
037 *             <properties name="newline-at-end" value="false"/>
038 *         </output-mapper>
039 *     }</pre><br>
040 *
041 * <b>NB. Do not set combine-spaces to true if you have data where spaces are significant.</b>
042 *
043 * @author Alwyn Schoeman
044 * @since 2.1.4
045 */
046public class RemoveNewLinesMapper implements ByteArrayMapper, Configurable {
047    /** Creates a mapper with default settings; tweak via {@link #setConfiguration(Configuration)}. */
048    public RemoveNewLinesMapper() {}
049    boolean combineSpaces = false;
050    boolean newLineAtEnd = true;
051    final byte SPACE = ' ';
052    byte[] NEWLINE_SEPARATORS = System.lineSeparator().getBytes();
053
054    @Override
055    public byte[] apply(byte[] bytes) {
056        final ByteBuffer buffer = ByteBuffer.allocate(bytes.length);
057        boolean prevSpace = false;
058        for (byte aByte : bytes) {
059            if (aByte == SPACE) {
060                if (combineSpaces) {
061                    prevSpace = true;
062                } else {
063                    buffer.put(aByte);
064                }
065            } else {
066                if (combineSpaces && prevSpace) {
067                    buffer.put(SPACE);
068                    prevSpace = false;
069                }
070                if (!isNewLine(aByte)) {
071                    buffer.put(aByte);
072                }
073            }
074        }
075        // Solve the mystery of the missing trailing spaces.
076        if (combineSpaces && prevSpace) {
077            buffer.put(SPACE);
078        }
079        if (newLineAtEnd) {
080            buffer.put(NEWLINE_SEPARATORS);
081        }
082        buffer.flip();
083        byte[] result = new byte[buffer.limit()];
084        buffer.get(result);
085        return result;
086    }
087
088    @Override
089    public void setConfiguration(Configuration cfg) throws ConfigurationException {
090        combineSpaces = cfg.getBoolean("combine-spaces", false);
091        newLineAtEnd = cfg.getBoolean("newline-at-end", true);
092    }
093
094    private boolean isNewLine(byte aByte) {
095        boolean isNewLine = false;
096        for (byte b : NEWLINE_SEPARATORS) {
097            if (aByte == b) {
098                isNewLine = true;
099                break;
100            }
101        }
102        return isNewLine;
103    }
104}