// // Copyright (c)1998-2011 Pearson Education, Inc. or its affiliate(s). // All rights reserved. // package openadk.library.impl; import java.io.*; /** * A MessageStreamer is a Reader class used to assemble and stream a SIF_Message * by concatenating an outer <i>envelope</i> with one or more inner <i>payloads</i>. * The MessageStreamer can be passed to any method that accepts a Reader as an * input source.<p> * * MessageStreamer takes care of assembling the envelope and payload by reading * from the Envelope Reader and Payload Reader passed to the constructor. * Bytes are read from the Envelope Reader up until the named element is * encountered (inclusive). Subsequent bytes are read from the Payload Reader(s) * until the closing tag of the named element is encountered (also inclusive). * Finally, the remaining bytes of the Envelope Reader are read.<p> * * An instance of this class is typically passed to the IProtocolHandler.send * method when sending an arbitrarily large SIF_Message. * * @author Eric Petersen * @version ADK 1.0 */ public class MessageStreamer extends Reader { private int fPos = 0; private int fStage = 0; protected Reader fEnvelope; protected Reader[] fPayloads; protected int fPayloadIndex = -1; protected String fElement; protected boolean fReplaceMode = false; protected boolean fReady = true; /** * Constructor * @param envelope A Reader that provides the content of the <i>envelope</i> * @param payload An array of Readers that provide the content of the <i>payload</i> * @param element The element that signals the beginning of the payload. * The first element of this type will be considered the beginning of * the payload. */ public MessageStreamer( Reader envelope, Reader[] payloads, String element ) { fEnvelope = envelope; fPayloads = payloads; fElement = element; } /** * Turns <i>replace</i> mode on or off. When this mode is enabled, the * element is replaced with the supplied payload instead of inserting the * payload as a child of the element. */ public void setReplaceMode( boolean enable ) { fReplaceMode = enable; } public synchronized int read( char[] cbuf, int off, int len ) throws IOException { /* In replace mode, the open tag is skipped in the payload, * but the close tag of the payload is written out. Then in * stage 2, the close tag in the envelope is skipped. This * means there can only be one payload in replace mode. */ if ( fReplaceMode && fPayloads.length > 1 ) throw new IllegalArgumentException( "In replace mode, MessageStreamer can only handle one payload." ); if( len == 0 ) return 0; int bytes = -1; switch( fStage ) { case 0: /* Read initial portion of Envelope */ { int start = -1; int pos = off; // Read from the Envelope Reader, looking for <element> to signal // advancement to the next stage do { cbuf[pos] = (char)fEnvelope.read(); if( cbuf[pos] == '<' ) start = pos; else if( cbuf[pos] == '>' ) { String cmp = new String(cbuf,start,(pos-start)+1); if( cmp.equals(fElement) ) { fStage++; return( pos-off ) + 1; } } pos++; } while( cbuf[pos-1] != -1 && pos < len ); } break; case 1: /* Read entire Payload */ /* * TT 919 - When in replace mode, we were not handling payloads over * 1024 bytes long correctly because we were skipping the element tag * once for each 1024 bytes, rather than just once at the beginning * of the payload. (1024 comes from the len passed in.) Changed this * so that the fPayloadIndex starts at -1. Then the advancement is the * same more all payloads. When the advancement is done, then we skip * the element if necessary. Note that the end tag is written out * in stage 1. In stage 2, the end tag is skipped. This means that * this code can really only accept one payload if in replace mode. */ if ( fPayloadIndex >= 0 ) bytes = fPayloads[ fPayloadIndex ].read(cbuf,off,len); if( bytes <= 0 /* == -1 */ ) { // Advance to the next Payload, or if there isn't one, to // the next stage if( ++fPayloadIndex == fPayloads.length ) fStage++; else if ( fReplaceMode ) fPayloads[ fPayloadIndex ].skip(fElement.length()); return 0; } else return bytes; case 2: /* Read remaining portion of Envelope */ if( fReplaceMode ) fEnvelope.skip(fElement.length() + 2 /* 2 for the < and > */ ); bytes = fEnvelope.read(cbuf,off,len); if( bytes == -1 ) { fStage++; fReady = false; return 0; } else return bytes; } return -1; } public synchronized void close() throws IOException { fEnvelope.close(); for( int i = 0; i < fPayloads.length; i++ ) fPayloads[i].close(); } public synchronized boolean ready() { return fReady; } }