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}