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.core.ConfigurationException;
022import org.jpos.core.SimpleConfiguration;
023import org.jpos.iso.ISOBasePackager;
024import org.jpos.iso.ISOBaseValidator;
025import org.jpos.iso.ISOComponent;
026import org.jpos.iso.ISOException;
027import org.jpos.iso.ISOFieldPackager;
028import org.jpos.iso.ISOFieldValidator;
029import org.jpos.iso.ISOMsgFieldPackager;
030import org.jpos.iso.ISOMsgFieldValidator;
031import org.jpos.iso.ISOValidator;
032import org.jpos.iso.validator.ISOVException;
033import org.jpos.util.LogEvent;
034import org.jpos.util.Logger;
035import org.xml.sax.Attributes;
036import org.xml.sax.SAXException;
037import org.xml.sax.SAXParseException;
038import org.xml.sax.XMLReader;
039import org.xml.sax.helpers.DefaultHandler;
040import org.xml.sax.helpers.XMLReaderFactory;
041
042import java.io.InputStream;
043import java.util.ArrayList;
044import java.util.List;
045import java.util.Map;
046import java.util.Map.Entry;
047import java.util.Properties;
048import java.util.Stack;
049import java.util.TreeMap;
050
051
052/**
053 * Generic Packager that configure validators too.
054 * <p>Title: jPOS</p>
055 * <p>Description: Java Framework for Financial Systems</p>
056 * <p>Copyright: Copyright (c) 2000 jPOS.org.  All rights reserved.</p>
057 * <p>Company: www.jPOS.org</p>
058 * @author Jose Eduardo Leon
059 * @version 1.0
060 */
061@SuppressWarnings("unchecked")
062public class GenericValidatingPackager extends GenericPackager implements ISOValidator {
063
064    public GenericValidatingPackager(  ) throws ISOException{
065        super();
066    }
067    public GenericValidatingPackager( String fileName ) throws ISOException {
068        super( fileName );
069    }
070    public GenericValidatingPackager (InputStream stream) throws ISOException {
071        super (stream);
072    }
073
074    /**
075     * Convert the ISOFieldPackagers in the Map
076     * to an array of ISOFieldPackagers
077     */
078    private ISOFieldPackager[] makeFieldArray(Map<Integer,ISOFieldPackager> m)
079    {
080        int maxField = 0;
081
082        // First find the largest field number in the Map
083        for (Entry<Integer,ISOFieldPackager> ent :m.entrySet())
084            if (ent.getKey() > maxField)
085                maxField = ent.getKey();
086
087        // Create the array
088        ISOFieldPackager fld[] = new ISOFieldPackager[maxField+1];
089
090        // Populate it
091        for (Entry<Integer,ISOFieldPackager> ent :m.entrySet())
092           fld[ent.getKey()] = ent.getValue();
093        return fld;
094    }
095
096    /**
097     * It define GenericValidatorContentHandler like handler.
098     */
099    public void readFile(String filename) throws org.jpos.iso.ISOException {
100        try {
101            XMLReader reader = XMLReaderFactory.createXMLReader(
102                    System.getProperty( "sax.parser",
103                    "org.apache.crimson.parser.XMLReaderImpl"));
104            reader.setFeature ("http://xml.org/sax/features/validation", true);
105            GenericValidatorContentHandler handler = new GenericValidatorContentHandler();
106            reader.setContentHandler(handler);
107            reader.setErrorHandler(handler);
108            reader.setEntityResolver(new GenericEntityResolver());
109            reader.parse(filename);
110        }
111        catch (Exception e)
112        {
113            e.printStackTrace();
114            throw new ISOException(e);
115        }
116    }
117    @Override
118    public void setGenericPackagerParams ( Attributes atts ) {
119        String maxField  = atts.getValue( "maxValidField" );
120        String emitBmap  = atts.getValue( "emitBitmap" );
121        String bmapfield = atts.getValue( "bitmapField" );
122        if ( maxField != null )
123            maxValidField = Integer.parseInt( maxField );
124        if ( emitBmap != null )
125            emitBitmap = Boolean.valueOf(emitBmap);
126        if ( bmapfield != null )
127            bitmapField = Integer.parseInt( bmapfield );
128    }
129
130    public void setMsgValidator( ISOBaseValidator[] msgVlds ){
131        this.mvlds = msgVlds;
132    }
133
134    public void setFieldValidator( ISOFieldValidator[] fvlds ){
135        this.fvlds = fvlds;
136    }
137
138    public ISOComponent validate(ISOComponent m) throws ISOException {
139        LogEvent evt = new LogEvent( this, "validate" );
140        try {
141            ISOComponent c;
142            Map<Object,ISOComponent> fields = m.getChildren();
143            /** Field  validations **/
144            for (ISOValidator val :fvlds) {
145                if ( (c=fields.get (((ISOFieldValidator) val).getFieldId())) != null ){
146                    try {
147                        m.set( val.validate( c ) );
148                    } catch ( ISOVException e ) {
149                        if ( !e.treated() ) {
150                            m.set( e.getErrComponent() );
151                            e.setTreated( true );
152                        }
153                        evt.addMessage( "Component Validation Error." );
154                        throw e;
155                    }
156                }
157            }
158            /** msg validations **/
159            try {
160                for (ISOBaseValidator mval :mvlds)
161                    m = mval.validate( m );
162            }
163            catch (ISOVException ex) {
164                evt.addMessage( "Component Validation Error." );
165                throw ex;
166            }
167            return m;
168        }
169        finally {
170            Logger.log( evt );
171        }
172    }
173
174/*  Values copied from ISOBasePackager
175These can be changes using attributes on the isopackager node */
176    protected  int maxValidField=128;
177    protected boolean emitBitmap=true;
178    protected int bitmapField=1;
179    /** FieldValidator array. **/
180    protected ISOValidator[] fvlds = {};
181    /** MsgValidator array **/
182    protected ISOBaseValidator[] mvlds = {};
183    /** incr used to put validators in the same hashtable of
184     *  fieldpackagers. packagers will stay on index 1, 2, 3...
185     *  and validators in inc+1, inc+2, inc+3,... **/
186    static final int inc = 500;
187
188
189    @SuppressWarnings("unchecked")
190    public class GenericValidatorContentHandler extends DefaultHandler {
191        @Override
192        public void startDocument(){
193            fieldStack = new Stack<Object>();
194            validatorStack = new Stack<Object>();
195        }
196
197        @Override
198        public void endDocument() throws SAXException {
199            if ( !fieldStack.isEmpty() )
200                throw new SAXException ( "Format error in XML Field Description File" );
201        }
202
203        @Override
204        public void startElement( String namespaceURI, String localName, String qName, Attributes atts )
205                throws SAXException {
206            try {
207                if ( localName.equals( "isopackager" ) ) {
208                    // Stick a new Map on stack to collect the fields
209                    fieldStack.push( new TreeMap() );
210
211                    /** used to insert msg-level validators **/
212                    Map m = new TreeMap();
213                    m.put(VALIDATOR_INDEX, new ArrayList() );
214
215                    validatorStack.push( m );
216                    setGenericPackagerParams ( atts );
217                }
218                if (localName.equals("isofield")){
219                    /** getID global for isofieldvalidator **/
220                    fldID = atts.getValue("id");
221                    String type = atts.getValue("class");
222                    String name = atts.getValue("name");
223                    String size = atts.getValue("length");
224                    String pad  = atts.getValue("pad");
225                    Class c = Class.forName(type);
226                    ISOFieldPackager f;
227                    f = (ISOFieldPackager) c.newInstance();
228                    f.setDescription(name);
229                    f.setLength(Integer.parseInt(size));
230                    f.setPad(Boolean.parseBoolean(pad));
231                    // Insert this new isofield into the Map
232                    // on the top of the stack using the fieldID as the key
233                    Map m = (Map) fieldStack.peek();
234                    m.put(Integer.valueOf(fldID), f);
235                }
236                if ( localName.equals( "isofieldvalidator" ) ){
237                    String type = atts.getValue( "class" );
238                    String breakOnError = atts.getValue( "break-on-error" );
239                    String minLen = atts.getValue( "minlen" );
240                    String maxLen = atts.getValue( "maxlen" );
241                    Class c = Class.forName( type );
242                    ISOFieldValidator v = (ISOFieldValidator)c.newInstance();
243                    if ( breakOnError != null ) v.setBreakOnError(Boolean.valueOf(breakOnError));
244                    if ( minLen != null ) v.setMinLength( Integer.parseInt( minLen ) );
245                    if ( maxLen != null ) v.setMaxLength( Integer.parseInt( maxLen ) );
246                    v.setFieldId( Integer.parseInt(fldID) );
247                    /** insert validator on stack waiting for properties **/
248                    validatorStack.push( v );
249                    validatorStack.push( new Properties() );
250                }
251                if ( localName.equals( "property" ) ){
252                    ((Properties)validatorStack.peek()).setProperty(
253                            atts.getValue( "name" ),
254                            atts.getValue( "value" ) );
255                }
256                if ( localName.equals( "isovalidator" ) ){
257                    String type = atts.getValue( "class" );
258                    String breakOnError = atts.getValue( "break-on-error" );
259                    Class c = Class.forName( type );
260                    ISOBaseValidator v = (ISOBaseValidator)c.newInstance();
261                    if ( breakOnError != null ) v.setBreakOnError(Boolean.valueOf(breakOnError));
262                    /** insert validator on stack waiting for properties **/
263                    validatorStack.push( v );
264                    validatorStack.push( new Properties() );
265                }
266                if ( localName.equals("isofieldpackager") ) {
267                    String id   = atts.getValue("id");
268                    String type = atts.getValue("class");
269                    String name = atts.getValue("name");
270                    String size = atts.getValue("length");
271                    String pad  = atts.getValue("pad");
272/*
273For a isofield packager node push the following fields
274onto the stack.
2751) an Integer indicating the field ID
2762) an instance of the specified ISOFieldPackager class
2773) an instance of the specified ISOBasePackager (msgPackager) class
2784) a Map to collect the subfields
279*/
280                    String packager = atts.getValue("packager");
281                    fieldStack.push(Integer.valueOf(id));
282                    ISOFieldPackager f;
283                    f = (ISOFieldPackager) Class.forName(type).newInstance();
284                    f.setDescription(name);
285                    f.setLength(Integer.parseInt(size));
286                    f.setPad(Boolean.parseBoolean(pad));
287                    fieldStack.push(f);
288                    ISOBasePackager p;
289                    p = (ISOBasePackager) Class.forName(packager).newInstance();
290                    if (p instanceof GenericValidatingPackager){
291                        GenericValidatingPackager gp = (GenericValidatingPackager) p;
292                        gp.setGenericPackagerParams (atts);
293                    }
294                    fieldStack.push(p);
295                    String validator = atts.getValue( "validator" );
296                    ISOBaseValidatingPackager v;
297                    v = (ISOBaseValidatingPackager) Class.forName(validator).newInstance();
298                    validatorStack.push( v );
299                    Map m = new TreeMap();
300                    m.put(VALIDATOR_INDEX, new ArrayList() );
301                    validatorStack.push( m );
302                    fieldStack.push( new TreeMap() );
303                }
304            } catch (Exception ex){
305                throw new SAXException(ex);
306            }
307        }
308
309        /**
310         * Convert the ISOFieldPackagers in the Map
311         * to an array of ISOFieldPackagers
312         */
313        private ISOFieldPackager[] makeFieldPackArray(Map<Integer,ISOFieldPackager> m){
314            int maxField = 0;
315            // First find the largest field number in the Map
316            for (Entry<Integer,ISOFieldPackager> ent :m.entrySet())
317                if (ent.getKey() > maxField)
318                    maxField = ent.getKey();
319            // Create the array
320            ISOFieldPackager fld[] = new ISOFieldPackager[maxField+1];
321            // Populate it
322            for (Entry<Integer,ISOFieldPackager> ent :m.entrySet())
323               fld[ent.getKey()] = ent.getValue();
324            return fld;
325        }
326
327        @Override
328        public void endElement(String namespaceURI, String localName, String qName) {
329            if (localName.equals("isopackager")){
330                Map m  = (Map)fieldStack.pop();
331                setFieldPackager( makeFieldPackArray(m) );
332                m = (Map)validatorStack.pop();
333                setFieldValidator ( makeFieldValidatorArray( m ));
334                setMsgValidator( makeMsgValidatorArray( m ) );
335            }
336            if ( localName.equals( "isofieldvalidator" ) ){
337                /** pop properties **/
338                Properties p = (Properties)validatorStack.pop();
339                SimpleConfiguration cfg = null;
340                if ( !p.entrySet().isEmpty() )
341                    cfg = new SimpleConfiguration( p );
342                /** pop validator and add it to the hash **/
343                ISOFieldValidator f = (ISOFieldValidator)validatorStack.pop();
344                if ( cfg != null ){
345                    try {
346                        f.setConfiguration( cfg );
347                    }
348                    catch (ConfigurationException ex) {
349                        ex.printStackTrace(  );
350                    }
351                }
352                ((Map)validatorStack.peek()).put(Integer.valueOf(fldID), f );
353            }
354            if ( localName.equals( "isovalidator" ) ){
355                /** pop properties **/
356                Properties p = (Properties)validatorStack.pop();
357                SimpleConfiguration cfg = null;
358                if ( !p.entrySet().isEmpty() )
359                    cfg = new SimpleConfiguration( p );
360                /** pop validator and add it to the hash **/
361                ISOBaseValidator v = (ISOBaseValidator)validatorStack.pop();
362                if ( cfg != null ){
363                    try {
364                        v.setConfiguration( cfg );
365                    }
366                    catch (ConfigurationException ex) {
367                        ex.printStackTrace(  );
368                    }
369                }
370                /** add validator to the has **/
371                ((List)((Map)validatorStack.peek()).get(VALIDATOR_INDEX)).add( v );
372            }
373            if (localName.equals("isofieldpackager")){
374                // Pop the 4 entries off the stack in the correct order
375                Map m = (Map)fieldStack.pop();
376                ISOBasePackager msgPackager = (ISOBasePackager) fieldStack.pop();
377                msgPackager.setFieldPackager (makeFieldArray(m));
378                msgPackager.setLogger (getLogger(), "Generic Packager");
379                ISOFieldPackager fieldPackager = (ISOFieldPackager) fieldStack.pop();
380                Integer fno = (Integer) fieldStack.pop();
381                // Create the ISOMsgField packager with the retrieved msg and field Packagers
382                ISOMsgFieldPackager mfp =
383                        new ISOMsgFieldPackager(fieldPackager, msgPackager);
384
385                // Add the newly created ISOMsgField packager to the
386                // lower level field stack
387                m=(Map)fieldStack.peek();
388                m.put(fno, mfp);
389                Map val = (Map)validatorStack.pop();
390                ISOBaseValidatingPackager v = (ISOBaseValidatingPackager) validatorStack.pop();
391                v.setFieldValidator( makeFieldValidatorArray ( val ) );
392                v.setMsgValidator( makeMsgValidatorArray ( val ) );
393                ISOMsgFieldValidator mfv = new ISOMsgFieldValidator ( fieldPackager.getDescription(), v );
394                mfv.setFieldId(fno);
395                v.setLogger (getLogger(), "Generic validating Packager");
396                m=(Map)validatorStack.peek();
397                m.put(fno, mfv);
398            }
399        }
400
401        ISOFieldValidator[] makeFieldValidatorArray ( Map<Integer,ISOFieldValidator> m ){
402            List<ISOFieldValidator> l = new ArrayList();
403            // Populate it
404            for (Entry<Integer,ISOFieldValidator> ent :m.entrySet() )
405                if ( ent.getKey() != VALIDATOR_INDEX )
406                    l.add(ent.getValue());
407            // Create the array
408            return l.toArray(new ISOFieldValidator[l.size()]);
409        }
410
411        ISOBaseValidator[] makeMsgValidatorArray ( Map m ){
412            // First find the count
413            List<ISOBaseValidator> l = (List)m.get(VALIDATOR_INDEX);
414            return l.toArray(new ISOBaseValidator[l.size()]);
415        }
416
417        // ErrorHandler Methods
418        @Override
419        public void error (SAXParseException ex) throws SAXException
420        {
421            throw ex;
422        }
423
424        @Override
425        public void fatalError (SAXParseException ex) throws SAXException
426        {
427            throw ex;
428        }
429
430        static final int VALIDATOR_INDEX = -3 ;
431        private Stack<Object> fieldStack, validatorStack;
432        private String fldID;
433
434    }
435}