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