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