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.iso.packager;
020
021import org.jpos.iso.*;
022import org.jpos.util.LogEvent;
023import org.jpos.util.LogSource;
024import org.jpos.util.Logger;
025
026import java.io.ByteArrayOutputStream;
027import java.util.Map;
028import java.util.Map.Entry;
029import java.util.Set;
030
031/**
032 * EuroPay SubField packager
033 * @author Eoin Flood
034 * @version $Revision$ $Date$
035 * @see ISOPackager
036 * @see ISOBasePackager
037 * @see ISOComponent
038 *
039 * This packager is used by EuroPackager to package subfields
040 * such as field 48.
041 */
042@SuppressWarnings("unchecked")
043public class EuroSubFieldPackager extends ISOBasePackager
044{
045    /** Default constructor; no instance state to initialise. */
046    public EuroSubFieldPackager() {}
047    /** Length prefixer used to pack/unpack each Euro subfield's tag. */
048    protected static Prefixer tagPrefixer = AsciiPrefixer.LL;
049
050    /**
051     * Always return false
052     */
053    @Override
054    protected boolean emitBitMap()
055    {
056        return false;
057    }
058
059    @Override
060    public byte[] pack (ISOComponent c) throws ISOException {
061        LogEvent evt = new LogEvent (this, "pack");
062        try (ByteArrayOutputStream bout = new ByteArrayOutputStream(100)) {
063            Map tab = c.getChildren();
064
065            for (Entry ent : (Set<Entry>) tab.entrySet()) {
066                Integer i = (Integer) ent.getKey();
067                if (i < 0)
068                    continue;
069                if (fld[i] == null)
070                    throw new ISOException ("Unsupported sub-field " + i + " packing field " + c.getKey());
071                if (ent.getValue() instanceof ISOComponent)
072                    try {
073                        ISOComponent f = (ISOComponent) ent.getValue();
074                        byte[] b = fld[i].pack(f);
075                        bout.write(b);
076                    } catch (Exception e) {
077                        evt.addMessage ("error packing subfield "+i);
078                        evt.addMessage (c);
079                        evt.addMessage (e);
080                        throw e;
081                    }
082            }
083
084            byte[] d = bout.toByteArray();
085            if (logger != null)  // save a few CPU cycle if no logger available
086                evt.addMessage (ISOUtil.hexString (d));
087            return d;
088        }
089        catch (Exception ex)
090        {
091            throw new ISOException (ex);
092        }
093    }
094
095
096    /**
097     * This packager treats field 0 as a field that may or may not be present before the  TLV subelements.
098     *
099     * Certain types of messages for some 8583 specs that extend this class' behavior (e.g., the Mastercard implementation
100     * in class {@link MasterCardEBCDICSubFieldPackager}) may not have field 0 present (the TCC in Mastercard's nomenclature).
101     * So, if the corresponding isofield packager for field 0 doesn't fill the {@link ISOComponent}'s value,
102     * we don't store anything as subfield 0 of m.
103     */
104    @Override
105    public int unpack (ISOComponent m, byte[] b) throws ISOException
106    {
107        LogEvent evt = logger != null ? new LogEvent (this, "unpack") : null;
108        int consumed = 0;
109        ISOComponent c = null;
110
111        // Unpack the fields
112        while (consumed < b.length) {
113            //If this is first iteration and there is a packager for SE 0 then i=0, i.e. use field packager for SE 0
114            //Else determine current tag
115            int i = c == null && fld[0] != null ? 0 : tagPrefixer.decodeLength(b, consumed);
116
117            if (i >= fld.length || fld[i] == null)
118                throw new ISOException("Unsupported sub-field " + i + " unpacking field " + m.getKey());
119
120            c = fld[i].createComponent(i);
121            consumed += fld[i].unpack (c, b, consumed);
122            if (i != 0 || c.getValue() != null) {
123                if (evt != null) {
124                    fieldUnpackLogger(evt, i, c, fld[i], logFieldName);
125                }
126
127                m.set(c);
128            }
129            // else:
130            // If it was field 0 (TCC) && nothing was stored in the component, we discard this component
131        }
132
133        if (logger != null && evt != null)
134            Logger.log (evt);
135
136        return consumed;
137    }
138
139
140    @Override
141    public void setLogger (Logger logger, String realm) {
142        super.setLogger (logger, realm);
143        if (fld != null) {
144            for (int i=0; i<fld.length; i++) {
145                if (fld[i] instanceof ISOMsgFieldPackager) {
146                    Object o = ((ISOMsgFieldPackager)fld[i]).getISOMsgPackager();
147                    if (o instanceof LogSource) {
148                        ((LogSource)o).setLogger (logger, realm + "-fld-" + i);
149                    }
150                }
151            }
152        }
153    }
154
155}
156