com.prowidesoftware.swift.io.parser.SwiftParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of wife Show documentation
Show all versions of wife Show documentation
Prowide Core Libraries for SWIFT (TM) messages
The newest version!
/*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package com.prowidesoftware.swift.io.parser;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import com.prowidesoftware.swift.WifeException;
import com.prowidesoftware.swift.model.*;
/**
* FIN Parser. This implementation now properly supports all system messages (i.e: messages for MT 0xx) and
* service messages (for example: ACK).
* As part of this, the following is now also accepted:
*
* - Block 4 may be a non-text block (for example: {4:{101:xx}{102:xx}})
*
- Support for unparsed texts (at message, block and tag levels)
*
- Support for user defined blocks (for example: {S:{T01:xxx}{T02:yyy}})
*
* This is based in the old SwiftParser2, that is now deprecated.
*
* @author www.prowidesoftware.com
* @version $Id: SwiftParser.java,v 1.15 2012/06/16 04:01:34 mikkey Exp $
*/
public class SwiftParser {
/**
* Helper constant with the content of System.getProperty("line.separator", "\n")
*/
public static final String EOL = System.getProperty("line.separator", "\n");
private static transient final java.util.logging.Logger log = java.util.logging.Logger.getLogger(SwiftParser.class.getName());
private Reader reader;
private StringBuffer buffer;
/**
* Reference to the current message being parsed.
* This should be used when some parsing decision needs to be made based on a previous item parsed,
* like a value in a previous tag or block.
*/
private SwiftMessage currentMessage;
/**
* Errors found while parsing the message.
*/
private final List errors = new ArrayList();
private int lastBlockStartOffset = 0;
/**
* Constructor with an input stream for parsing a message
* @param is stream to read
*/
public SwiftParser(final InputStream is) {
this(new InputStreamReader(is));
}
/**
* Constructor with a reader for parsing a message
* @param r the Reader with the swift message to read
*/
public SwiftParser(final Reader r) {
setReader(r);
}
/**
* Constructor with a reader for parsing a message
* @param message the String with the swift message to read
*/
public SwiftParser(final String message) {
this(new StringReader(message));
}
/**
* default constructor.
* NOTE: If this constructor is called, setReader must be called to use the parser
*/
public SwiftParser() {
}
/**
* sets the input reader.
* NOTE: this resets the internal buffer
* @param r the reader to use
*/
public void setReader(final Reader r) {
this.buffer = new StringBuffer();
this.reader = r;
}
/**
* sets the input data to the received string.
* @param data the data to use as input
*/
public void setData(final String data) {
setReader(new StringReader(data));
}
/**
* Parse a SWIFT message into a data structure
*
* @return the parsed swift message object
* @throws IOException
*/
public SwiftMessage message() throws IOException {
// create a message and store for local reference
final SwiftMessage message = new SwiftMessage(false);
this.currentMessage = message;
// Clear all errors before starting the parse process
this.errors.clear();
try {
boolean done = false;
SwiftBlock b = null;
do {
// try to consume a block
if ((b = consumeBlock()) != null) {
if (log.isLoggable(Level.FINER)) {
log.finer("consumed block: " + b);
}
this.currentMessage.addBlock(b);
} else {
if (log.isLoggable(Level.FINER)) {
log.finer("no block consumed");
}
done = true;
}
} while (!done);
} finally {
// Clean the reference to the message being parsed
this.currentMessage = null;
}
return (message);
}
/**
* Sets the parameter string as this parser data and returns the parsed object.
*
* @param message the String with the swift message to parse
* @return the parsed swift message object
* @throws IOException
*
* @since 6.0
*/
public SwiftMessage parse(final String message) throws IOException {
setData(message);
return message();
}
/**
* Consume the next block of the message on the reader.
* This methods seeks to a block start, then identifies the block
* and calls the proper method to consume the block type
* that is coming, not all blocks are parsed in the same manner.
* @param blockHint
*
* @return the next block in the reader or null
if none was found (i.e: end of input)
* @throws IOException if an error occurred during read
*/
protected SwiftBlock consumeBlock() throws IOException {
// search for block start
if (log.isLoggable(Level.FINEST)) {
log.finest("consumeBlock: findBlockStart()");
}
findBlockStart();
if (log.isLoggable(Level.FINEST)) {
log.finest("block start found");
}
// read the block contents
final String s = readUntilBlockEnds();
if (log.isLoggable(Level.FINEST)) {
log.finest("block buffer: [" + s + "]");
}
if (s.equals("")) {
if (log.isLoggable(Level.FINEST)) {
log.finest("end of input");
}
return (null);
}
// analyze if it is an unparsed text
//
// NOTE: This can happen when we have got a block 1 and there is already a block 1
//
if (s.startsWith("1:")) {
if (log.isLoggable(Level.FINEST)) {
log.finest("Possible unparsed text");
}
// check if the block 1 is already here
if (this.currentMessage != null && this.currentMessage.getBlock1() != null) {
// unparsed text => initialize value to append
if (log.isLoggable(Level.FINEST)) {
log.finest("It is an unparsed text");
}
final StringBuffer utBuffer = new StringBuffer();
utBuffer.append("{");
utBuffer.append(s);
utBuffer.append("}");
boolean done = false;
while (!done) {
// try to read a block of data
final char data[] = new char[128];
final int size = this.reader.read(data);
if (size > 0) {
// append the read buffer
utBuffer.append(data);
} else {
// we are done
done = true;
}
}
final String unparsedText = utBuffer.toString();
if (log.isLoggable(Level.FINEST)) {
log.finest("unparsed texts to process: [" + unparsedText + "]");
}
// build an unparsed text list
final UnparsedTextList list = processUnparsedText(unparsedText);
if (list != null) {
this.currentMessage.setUnparsedTexts(list);
}
// no more reading
return (null);
} else {
if (log.isLoggable(Level.FINEST)) {
log.finest("Regular block");
}
}
}
// identify and create the proper block
final char blockId = identifyBlock(s);
if (log.isLoggable(Level.FINEST)) {
log.finest("blockId: " + blockId);
}
SwiftBlock b = null;
if (blockId == ' ') {
// block cannot be identified
if (log.isLoggable(Level.SEVERE)) {
log.severe("A block could not be identified!");
log.severe("unidentified block:" + s);
}
throw new WifeException("The block " + s + " could not be identified");
}
// create the block object
switch (blockId) {
case '1': // block 1 (single valued)
b = new SwiftBlock1(s);
break;
case '2': // block 2 (single valued)
if (isInput(s)) {
b = new SwiftBlock2Input(s);
} else {
b = new SwiftBlock2Output(s);
}
break;
case '3': // block 3 (tag list)
b = tagListBlockConsume(new SwiftBlock3(), s);
break;
case '4': // block 4
if (isTextBlock(s)) {
b = block4Consume(new SwiftBlock4(), s);
} else {
b = tagListBlockConsume(new SwiftBlock4(), s);
}
break;
case '5': // block 5 (tag list)
b = tagListBlockConsume(new SwiftBlock5(), s);
break;
default: // user defined block (tag list)
b = tagListBlockConsume(new SwiftBlockUser(Character.toString(blockId)), s);
break;
}
if (log.isLoggable(Level.FINEST)) {
log.finest("Block consumed: " + b);
}
return (b);
}
/**
* adds an error to the error list
* @param string the error to add
*/
/*private void addError(String string) {
// TODO add error generation
}*/
/**
* Attempt to detect if block 2 refers to an input or output message
* @param s the block 2 value (as a FIN value)
* @return whether it's an input block 2 (true) or an output one (false)
*/
private boolean isInput(final String s) {
if (log.isLoggable(Level.FINEST)) {
log.finest("block 2 type detection: " + s);
}
// try to find out the in/out type
final int i = s.indexOf(':');
if (i >= 0 && (i + 1) < s.length()) {
if (log.isLoggable(Level.FINEST)) {
log.finest("checking if [" + Character.toUpperCase(s.charAt(i + 1)) + "] is input");
}
// check for input mark
return (Character.toUpperCase(s.charAt(i + 1)) == 'I');
}
if (log.isLoggable(Level.FINEST)) {
log.finest("assuming output block 2");
}
return (false);
}
/**
* consumes a tag list block (i.e: block 3, block 5 or user defined block)
*
* @param b the block to set up tags into
* @param s the block data to process
* @return the processed block (the parameter b)
* @throws IOException
*/
protected SwiftTagListBlock tagListBlockConsume(final SwiftTagListBlock b, final String s) throws IOException {
if (log.isLoggable(Level.FINEST)) {
log.finest("data to consume: " + s);
}
// start processing the block data
final int start = s.indexOf(':');
if (start >= 0 && (start + 1) < s.length()) {
final String data = s.substring(start + 1);
if (log.isLoggable(Level.FINEST)) {
log.finest("data: " + data);
}
/*
* Enter a loop that will read any block or inner data
* The idea is to accept equally these strings:
* {block1}{block2}
* data1{block1}data2{block2}data3
*/
for (int i = 0; i < data.length(); i++) {
final char c = data.charAt(i);
if (c == '{') {
final int end = data.indexOf('}', i);
if (end >= 0 && data.length() > end) {
final String inner = data.substring(i + 1, end);
// Seek the cursor to last 'processed' position
i = end;
final Tag t = new Tag(inner);
log.finest("" + t);
b.addTag(t);
}
} else {
// read all the characters until data end or a new '{'
int end;
for (end = i; end < data.length() && data.charAt(end) != '{'; end++) {
;
}
final String unparsedText = data.substring(i, end).trim();
if (log.isLoggable(Level.FINEST)) {
log.finest("possible block unparsed text: \"" + unparsedText + "\"");
}
if (!"".equals(unparsedText)) {
if (log.isLoggable(Level.FINEST)) {
log.finest("adding block unparsed text: \"" + unparsedText + "\"");
}
b.unparsedTextAddText(unparsedText);
} else {
if (log.isLoggable(Level.FINEST)) {
log.finest("ingoring empty trimed unparsed text");
}
}
i = end - 1;
}
}
}
if (log.isLoggable(Level.FINEST)) {
log.finest("processed block: " + b);
}
return (b);
}
/**
* Parses a block 4 from an input string. This method supports the two possible formats of
* a swift block 4:
*
* - Text mode: this is the common block 4 for categories 1 to 9.
*
- Tag mode: this is the same format as for blocks 3 and 5. This format is used by
* service messages (for example: ACK) and system messages (category 0).
*
*
* @param b the block to set up tags into
* @param s the block data to process
* @return the processed block (the parameter b)
* @throws IOException
*/
protected SwiftBlock4 block4Consume(final SwiftBlock4 b, String s) throws IOException {
/*
* Note that if the block4 is a text block last character is -, which is part of the EOB
* since the parser removes the last }
*/
if (log.isLoggable(Level.FINEST)) {
log.finest("block4Consume [" + s + "]");
}
// process by "tokenizing" the input, this meaning:
// - skip the "4:" (block identifier)
// - scan for a tag start character (maybe '{' or ':')
// - if start is '{' => find tag end by balancing braces => split tag (considering unparsed texts)
// - if start is ':' => find tag end as ':[X]' => split tag (no unparsed texts)
// - detect block end as '-}' or '}'
//
int start = 0;
if (s.charAt(start) == '4') {
start++;
}
if (s.charAt(start) == ':') {
start++;
}
final boolean isTextBlock = isTextBlock(s);
Tag lastTag = null;
// start processing tags
while (start < s.length()) {
if (log.isLoggable(Level.FINEST)) {
log.finest("parsing at: begin [" + start + "] buffer [" + s.substring(start) + "]");
}
// position ourselves at something meaningful
int begin = start;
char c = ' ', prev;
do {
prev = c;
c = s.charAt(start++);
} while (start < s.length() && c != ':' && c != '{' && !(prev=='-' && c == '}'));
if (log.isLoggable(Level.FINEST)) {
log.finest("position: begin [" + begin + "] current [" + start + "]");
}
// check if we must correct end of unparsed text by "-}" (we don't want "-" to be unparsed text)
int ignore = 0;
if (c == '}') {
if (s.charAt(start - 1) == '-') {
ignore = 1;
}
}
// check if we skiped an block unparsed text
String unparsedText = s.substring(begin, start - ignore - 1).trim();
if (log.isLoggable(Level.FINEST)) {
log.finest("possible block unparsed text: \"" + unparsedText + "\"");
}
if (!"".equals(unparsedText)) {
if (log.isLoggable(Level.FINEST)) {
log.finest("adding block unparsed text: \"" + unparsedText + "\"");
}
b.unparsedTextAddText(unparsedText);
} else {
if (log.isLoggable(Level.FINEST)) {
log.finest("ingoring empty trimed unparsed text");
}
}
// if no more buffer => terminate
if (start == s.length()) {
if (log.isLoggable(Level.FINEST)) {
log.finest("reached end of input, terminating");
}
continue;
}
// decide what are we looking at (notice that "-}" is detected by "}")
int end = 0;
String tag = null;
String tagUnparsedText = null;
switch (c) {
case '}':
// sanity check
if (start != s.length()) {
if (log.isLoggable(Level.FINEST)) {
log.finest("reached end of block with pending input [" + s.substring(start) + "]");
}
}
// force termination only if ending string is -}
if ((isTextBlock && ignore==1) || !isTextBlock) {
start = s.length();
}
System.out.println("exit by bracket");
break;
case ':':
// get the tag text
end = textTagEndBlock4(s, start, isTextBlock);
tag = s.substring(start, end);
break;
case '{':
// two things are possible here:
// A) this is an unparsed text (i.e: the tag id is 1)
// B) this is a valid tag (i.e: the tag id is not one)
if (s.startsWith("1:", start)) {
//
// CASE A (an unparsed text)
//
if (log.isLoggable(Level.FINEST)) {
log.finest("processing block unparsed text at [" + (start - 1) + "]");
}
// keep our position
begin = start > 0 ? start - 1 : 0;
end = begin + 1;
while (end < s.length() && !s.startsWith("{1:", end)) {
end = blockTagEnd(s, end + 1);
}
// get the unparsed text
unparsedText = s.substring(begin, end);
if (log.isLoggable(Level.FINEST)) {
log.finest("block unparsed text from [" + begin + "] to [" + end + "] is [" + unparsedText + "]");
}
// add the unparsed text
b.unparsedTextAddText(unparsedText);
} else {
//
// CASE B (a tag)
//
// get the tag text
end = blockTagEnd(s, start);
tag = s.substring(start, end - 1);
final int utPos = tag.indexOf("{1:");
if (utPos != -1) {
// separate unparsed texts from value
tagUnparsedText = tag.substring(utPos);
tag = tag.substring(0, utPos);
}
}
break;
} /* switch(c) */
// process the tag (only if we have a tag)
if (tag != null) {
if (log.isLoggable(Level.FINEST)) {
log.finest("position: begin [" + begin + "] current [" + start + "] end [" + end + "]");
log.finest("processing tag [" + tag + "]");
}
// process the tag
final Tag t = consumeTag(tag, tagUnparsedText);
if (log.isLoggable(Level.FINEST)) {
log.finest("consumed tag [" + t + "]");
}
if (t != null) {
b.addTag(t);
lastTag = t;
}
}
// continue processing from the end of this tag
start = end;
}
// Strip EOB from last tags value
stripEOB(lastTag);
if (log.isLoggable(Level.FINEST)) {
log.finest("processed block: " + b);
}
return (b);
}
private void stripEOB(Tag lastTag) {
if (lastTag != null) {
String v = lastTag.getValue();
if (v != null) {
/*
* In the parser we support both \r\n or \n as line separator
*/
if (v.endsWith("\r\n-"))
lastTag.setValue(v.substring(0, v.length()-3));
else if (v.endsWith("\n-")) {
lastTag.setValue(v.substring(0, v.length()-2));
}
}
}
}
/**
* finds the end of a text tag (i.e: ":TAG:VALUE"). This is used to parse block 4.
* The function search the string looking for the occurrence of any of the sequences:
*
* - "[LBR]:[X]"
* - "[LBR]}"
* - "[LBR]{"
* - "}"
*
* where "[LBR]" stands for any of: "[CR]", "[LF]" or "[CR][LF]"
* and "[X]" is any character other than [CR] and [LF].
* Then considers the end of the tag as NOT containing the found sequence.
* NOTE: the condition "-}" cannot happen because the terminating dash is already removed.
*
* renamed to state clearly that this search is only used in block4Consume
*
* @param s the FIN input text
* @param start the position to start analysis at
* @return the position where the tag ends (excluding the )
*/
protected int textTagEndBlock4(final String s, int start, boolean isTextBlock) {
if (log.isLoggable(Level.FINEST)) {
log.finest("textTagEnd: scan for text tag end starting at [" + start + "]");
}
// start scanning for tag end
for (; start < s.length(); start++) {
// check if we found tag end
char c = s.charAt(start);
if (log.isLoggable(Level.FINEST)) {
log.finest("textTagEnd: cheking char [" + c + "]");
}
if (c == '\r' || c == '\n') {
// keep this position
final int begin = start;
// repeat cause "\r\n", accept "\n\r" also
if ((start + 1) == s.length()) {
break;
}
c = s.charAt(++start);
if (log.isLoggable(Level.FINEST)) {
log.finest("textTagEnd: CR|LF cheking char [" + c + "]");
}
if (c == '\r' || c == '\n') {
if ((start +1) == s.length()) {
break;
}
c = s.charAt(++start);
}
if (log.isLoggable(Level.FINEST)) {
log.finest("textTagEnd: final CR|LF char to check [" + c + "]");
}
// if open brace => it's a proper tag end (mixing BLOCK and TEXT tags, rare but...)
// if closing brace => it's a proper tag end (because of block end)
if ((!isTextBlock) && (c == '{' || c == '}' )) {
// found it
start = begin;
break;
}
// if it's a colon followed by a character different than CR or LF (':x') => it's a proper tag end
// because we have reached a new line with a beginning new tag.
// Note: It is note sufficient to check for a starting colon because for some fields like
// 77E for example, it is allowed the field content to have a ':' as the second line
// of its content.
else if (c == ':' && !(start == s.length())) {
final char z = s.charAt(++start);
if (z != '\r' && z != '\n') {
// found it
start = begin;
break;
}
}
// not matched => skip current char and continue
start = begin;
continue;
}
// check if we found block end (as "-}")
if (c == '-') {
// check for closing brace
c = (start + 1) < s.length() ? s.charAt(start + 1) : ' ';
if (c == '}' && isTextBlock) {
break;
}
}
// check if we found block end (as "}")
if (c == '}' && !isTextBlock) {
break;
}
}
if (log.isLoggable(Level.FINEST)) {
log.finest("textTagEnd: found tag end at [" + start + "]");
}
return (start);
}
/**
* Finds the end of a block tag (i.e: "{TAG:VALUE}"). This is used to parse blocks other than 4.
* The function search the string looking for the occurrence of the sequence "}". It is important to
* note that curly braces are balanced along the search.
* @param s the FIN input text
* @param start the position to start analysis at
* @return the position where the tag ends (including the "}")
*/
private int blockTagEnd(final String s, int start) {
if (log.isLoggable(Level.FINEST)) {
log.finest("blockTagEnd: scan for text tag end starting at [" + start + "]");
}
// scan until end or end of string
int balance = 0;
char c;
do {
// analyze this position
switch ((c = s.charAt(start++))) {
case '{':
balance++;
break;
case '}':
balance--;
break;
}
} while (start < s.length() && (balance >= 0 || (balance == 0 && c != '}')));
if (log.isLoggable(Level.FINEST)) {
log.finest("blockTagEnd: found tag end at [" + start + "]");
}
return (start);
}
/**
* Process the input as a tag. That is: split name and value (and possibly unparsed texts).
* The received buffer contains only the pertinent data for the tag (name and value). Trailing
* [CR][LF] on the text MUST not be present.
*
* @param buffer the buffer containing the tag
* @param unparsedText the unparsed text to assign (use null
if none is wanted).
* This single text is fragmented in multiple texts if there are more than one message.
* @return a swift Tag
* @throws IOException
*/
protected Tag consumeTag(final String buffer, final String unparsedText) throws IOException {
if (log.isLoggable(Level.FINEST)) {
log.finest("consumeTag: buffer [" + buffer + "]");
log.finest("consumeTag: unparsedText [" + unparsedText + "]");
}
// separate name and value
final int sep = buffer.indexOf(':');
String name = null;
String value = null;
if (sep != -1) {
name = buffer.substring(0, sep);
value = buffer.substring(sep + 1);
} else {
value = buffer;
}
// ignore empty tags (most likely, an "{}" in an unparsed text...)
if ((name == null || name.equals("")) && (value == null || value.equals(""))) {
if (log.isLoggable(Level.FINEST)) {
log.finest("consumeTag: ignoring empty tag");
}
return (null); // no tag...
}
// remove terminating [CR][LF] (or any combination)
int size = value.length();
if (size > 0) {
final char c = value.charAt(size - 1);
if (c == '\r' || c == '\n') {
size--;
}
}
if (size > 0) {
final char c = value.charAt(size - 1);
if (c == '\r' || c == '\n') {
size--;
}
}
if (size != value.length()) {
value = value.substring(0, size);
}
if (log.isLoggable(Level.FINE)) {
log.log(Level.FINE, "consumeTag: name [{0}] value [{1}]", new Object[]{name, value});
}
// build the tag
//
// NOTE: if we will use different Tag classes, here is the instantiation point
//
final Tag t = new Tag();
t.setName(name);
t.setValue(value);
// if there is unparsed text => process it
if (unparsedText != null) {
t.setUnparsedTexts(processUnparsedText(unparsedText));
}
return (t);
}
/**
* this method receives a string that is a sequence of unparsed text and splits it into
* different unparsed texts. The algorithm is to split on message begin (i.e: "{1:" and
* balance curly braces). This last thing ensures that a single message with unparsed text
* inner messages is treated as one single unparsed text.
* That is:
*
*
* {1:...} -- message block 1
* {4:... -- message block 4
* {1:...} -- \
* {4:... -- | one single unparsed text
* {1:...} -- | for block 4
* {4:...} -- /
* }
* }
*
*
* @param unparsedText the unparsed text to split (this parameter cannot be null).
* @return the list of unparsed texts. This can be null if the input is the empty string.
*/
private UnparsedTextList processUnparsedText(final String unparsedText) {
if (log.isLoggable(Level.FINEST)) {
log.finest("processUnparsedText: len [" + unparsedText.length() + "] input [" + unparsedText + "]");
}
// prepare to process
UnparsedTextList list = null;
// we start a new unparsed text at every "{1:"
int start = 0;
while (start < unparsedText.length()) {
// find the block end (balancing braces)
if (log.isLoggable(Level.FINEST)) {
log.finest("processUnparsedText: starting scan at [" + start + "] len [" + unparsedText.length() + "]");
}
int end = start + 1;
while ((end + 1) < unparsedText.length() && !unparsedText.startsWith("{1:", end)) {
if (log.isLoggable(Level.FINEST)) {
log.finest("processUnparsedText: entering end [" + end + "]");
}
end = blockTagEnd(unparsedText, end + 1);
// include trailing white spaces
while (end < unparsedText.length() && Character.isWhitespace(unparsedText.charAt(end))) {
if (log.isLoggable(Level.FINEST)) {
log.finest("processUnparsedText: skip white space char at [" + unparsedText.charAt(end) + "] pos [" + end + "]");
}
end++;
}
}
// separate a text
final String text = unparsedText.substring(start, end).trim();
if (log.isLoggable(Level.FINEST)) {
log.finest("processUnparsedText: unparsed text is [" + text + "]");
}
if (!text.equals("")) {
// add it to the list (create it if needed)
if (list == null) {
list = new UnparsedTextList();
}
list.addText(text);
}
// continue with next text
start = end;
}
if (log.isLoggable(Level.FINEST)) {
log.finest("processUnparsedText: returning [" + list + "]");
}
return (list);
}
/**
* Identify the block to be consumed.
*
* @param s the block identifier
* @return the block identifier or a space if the block can not be identified
*/
protected char identifyBlock(final String s) {
if (log.isLoggable(Level.FINEST)) {
log.finest("identifyBlock('" + s + "')");
}
if (s != null && s.length() > 1) {
final char c = s.charAt(0);
if ('0' <= c && c <= '9') {
return (c);
}
if ('a' <= c && c <= 'z') {
return (c);
}
if ('A' <= c && c <= 'Z') {
return (c);
}
}
return (' ');
}
/**
* Reads the buffer until end of block is reached.
* The initial character of the block must be already consumed and the
* reader has to be ready to consume the first character inside the block
*
* This method assumes that the starting block character was consumed
* because that is required in order to identify the start of a block, and
* call this method which reads until this block ends.
*
* @return a string with the block contents
* @throws IOException
*/
protected String readUntilBlockEnds() throws IOException {
final int start = buffer==null?0:buffer.length();
int len = 0;
int c;
/*
* Best effort reading includes this End Of Block (EOB) logic:
* first proper end of block or last block start.
*/
boolean checkNested = true;
// starts holds the amount of blockstart chars encountered, when this method
// is called, the initial block start was consumed, and therefore, this is initialized in 1
// this is needed to be able to include inner {} inside blocks
int starts = 1;
boolean done = false;
int count=0;
Boolean isTextBlock = null;
while (!done) {
c = getChar();
// check if we can set the textblock flag first
if (isTextBlock == null && count++ >=3) {
isTextBlock = isTextBlock();
if (isTextBlock) {
checkNested = false;
}
}
// found EOF?
if (c == -1) {
if (log.isLoggable(Level.FINEST)) {
log.finest("Found EOF");
}
done = true;
} else {
if (checkNested && isBlockStart((char) c)) {
starts++;
}
if (isBlockEnd(isTextBlock, c)) {
if (checkNested) {
starts--;
if (starts == 0) {
done = true;
} else {
len++;
}
} else {
done = true;
}
} else {
len++;
if (log.isLoggable(Level.FINEST)) {
log.finest("len: " + len);
log.finest("block spans: " + buffer.substring(start));
}
}
}
}
final int end = start + len;
// check for unbalanced { and }
if (checkNested) {
if (starts != 0 && (end - start) > 0) {
if (log.isLoggable(Level.FINEST)) {
log.finest("unbalanced '{' and '}' inside block (starts=" + starts + ")");
}
}
}
if (log.isLoggable(Level.FINEST)) {
log.finest("start: " + start);
log.finest("end: " + end);
log.finest("substring from start: " + buffer.substring(start));
log.finest("len: " + len);
}
return (buffer.substring(start, end));
}
private boolean isTextBlock() {
// hack to report as block4 only text blocks 4 , check data in buffer
if (this.lastBlockStartOffset >=0 && buffer.length()>this.lastBlockStartOffset)
return isTextBlock(buffer.substring(this.lastBlockStartOffset));
return false;
}
/**
* Determines if the given string is the start of a textblock
*/
private boolean isTextBlock(String s) {
// hack to report as block4 only text blocks 4 , check data in buffer
if (s.length()<3)
return false;
final int offset;
if (s.charAt(0)=='{')
offset = 1;
else
offset = 0;
final char c1 = s.charAt(offset+0);
final char c2 = s.charAt(offset+1);
if (c1=='4' && c2==':'){
int c = offset+2;
char tmp;
while ((offset+c)= 0) {
buffer.append((char) c);
}
return c;
}
/**
* Get a copy of the errors found.
* users can manipulate this copy without affecting the original.
*
* @return the list of errors found
*/
public List getErrors() {
return new ArrayList(this.errors);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy