PredefinedIdent.java

/*
 * Copyright (C) 2019 sw4j.org
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.sw4j.tool.barcode.random.generator;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.sw4j.tool.barcode.random.config.EncodingConfig;
import org.sw4j.tool.barcode.random.decoder.ByteArrayDecoder;
import org.sw4j.tool.barcode.random.encoder.ByteArrayEncoder;

/**
 * <p>
 * This class contains the value and the encoded representations for a single ident.
 * </p>
 * <p>
 * This class is immutable.
 * </p>
 * @author Uwe Plonus &lt;u.plonus@gmail.com&gt;
 */
public class PredefinedIdent implements IdentValue {

    /**
     * <p>
     * The ident number.
     * </p>
     */
    private final String ident;

    /**
     * <p>
     * The raw value.
     * </p>
     */
    private final byte[] rawValue;

    /**
     * <p>
     * A map containing all encoded values for the ident. The key is the encoding and the value the encoded value.
     * </p>
     */
    private final Map<EncodingConfig, String> encoded;

    /**
     * <p>
     * Create a new {@code PredefinedIdent} and the value for the {@code ident}. The value will be encoded in all given
     * {@code encodings}.
     * </p>
     * @param ident the ident for which the random number will be generated for.
     * @param value the value of the ident.
     * @param encoding the encoding of the given value.
     * @param encodings the encodings that are used for the encoded representation of the random number.
     * @throws IllegalArgumentException if no encoder for a given encoding can be found.
     */
    public PredefinedIdent(final String ident, final String value, final EncodingConfig encoding,
            final Set<EncodingConfig> encodings) {
        this.ident = ident;
        ByteArrayDecoder decoder = ByteArrayDecoder.forEncoding(encoding.getType());
        if (decoder == null) {
            throw new IllegalArgumentException(
                    String.format("Cannot find a decoder for encoding %s", encoding.getType()));
        }
        rawValue = decoder.decode(value);
        encoded = new HashMap<>();
        encodings.forEach((enc) -> {
            String rawEncoding = enc.getType();
            int endIndex = -1;
            int startIndex = -1;
            boolean hasMinus = false;
            if (rawEncoding.matches(".+\\{\\d*-?\\d+\\}")) {
                Pattern p = Pattern.compile("(.+)\\{((\\d*)-)?(\\d+)\\}");
                Matcher m = p.matcher(rawEncoding);
                m.matches();
                rawEncoding = m.group(1);
                if (m.group(2) != null && m.group(2).length() > 1) {
                    startIndex = Integer.parseInt(m.group(2).substring(0, m.group(2).length() - 1));
                }
                String startIndexGroup = m.group(2);
                hasMinus = m.group(3) != null;
                endIndex = Integer.parseInt(m.group(4));
            }
            ByteArrayEncoder encoder = ByteArrayEncoder.forEncoding(rawEncoding);
            if (encoder == null) {
                throw new IllegalArgumentException(
                        String.format("Cannot find an encoder for encoding %s", rawEncoding));
            }
            String encodedValue = encoder.encode(rawValue);
            if (endIndex > 0) {
                if (hasMinus) {
                    if (startIndex >= 0) {
                        encodedValue = encodedValue.substring(startIndex, endIndex);
                    } else {
                        encodedValue = encodedValue.substring(encodedValue.length() - endIndex);
                    }
                } else {
                    encodedValue = encodedValue.substring(0, endIndex);
                }
            }
            if (enc.getSepCount() != null) {
                int partsCount = encodedValue.length() / enc.getSepCount();
                if (encodedValue.length() % enc.getSepCount() != 0) {
                    partsCount++;
                }
                List<String> parts = new ArrayList<>(partsCount);
                for (int i = 0; i < partsCount; i++) {
                    int endPos = (i + 1) * enc.getSepCount() < encodedValue.length() ? (i + 1) * enc.getSepCount() :
                            encodedValue.length();
                    parts.add(encodedValue.substring(i * enc.getSepCount(),
                            endPos));
                }
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < parts.size(); i++) {
                    if (i > 0) {
                        sb.append(enc.getSepChar());
                    }
                    sb.append(parts.get(i));
                }
                encodedValue = sb.toString();
            }
            encoded.put(enc, encodedValue);
        });
    }

    /**
     * <p>
     * Return the ident number.
     * </p>
     * @return the ident number.
     */
    @Override
    public String getIdent() {
        return ident;
    }

    /**
     * <p>
     * Return the raw random number generated.
     * </p>
     * @return the raw random number.
     */
    @Override
    public byte[] getValue() {
        return Arrays.copyOf(rawValue, rawValue.length);
    }

    /**
     * <p>
     * Return the random number in the given {@code encoding}. Only the encodings that were given during construction
     * can be returned.
     * </p>
     * @param encoding the encoding for which the encoded value should be returned.
     * @return the encoded value or {@code null} if the encoding is unknown.
     */
    @Override
    public String getEncoded(final EncodingConfig encoding) {
        return encoded.get(encoding);
    }

}