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>
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    boolean combineSpaces = false;
048    boolean newLineAtEnd = true;
049    final byte SPACE = ' ';
050    byte[] NEWLINE_SEPARATORS = System.lineSeparator().getBytes();
051
052    @Override
053    public byte[] apply(byte[] bytes) {
054        final ByteBuffer buffer = ByteBuffer.allocate(bytes.length);
055        boolean prevSpace = false;
056        for (byte aByte : bytes) {
057            if (aByte == SPACE) {
058                if (combineSpaces) {
059                    prevSpace = true;
060                } else {
061                    buffer.put(aByte);
062                }
063            } else {
064                if (combineSpaces && prevSpace) {
065                    buffer.put(SPACE);
066                    prevSpace = false;
067                }
068                if (!isNewLine(aByte)) {
069                    buffer.put(aByte);
070                }
071            }
072        }
073        // Solve the mystery of the missing trailing spaces.
074        if (combineSpaces && prevSpace) {
075            buffer.put(SPACE);
076        }
077        if (newLineAtEnd) {
078            buffer.put(NEWLINE_SEPARATORS);
079        }
080        buffer.flip();
081        byte[] result = new byte[buffer.limit()];
082        buffer.get(result);
083        return result;
084    }
085
086    @Override
087    public void setConfiguration(Configuration cfg) throws ConfigurationException {
088        combineSpaces = cfg.getBoolean("combine-spaces", false);
089        newLineAtEnd = cfg.getBoolean("newline-at-end", true);
090    }
091
092    private boolean isNewLine(byte aByte) {
093        boolean isNewLine = false;
094        for (byte b : NEWLINE_SEPARATORS) {
095            if (aByte == b) {
096                isNewLine = true;
097                break;
098            }
099        }
100        return isNewLine;
101    }
102}