FileDocCategorySizeDatePackage
RTFParser.javaAPI DocJava SE 5 API8913Fri Aug 26 14:58:20 BST 2005javax.swing.text.rtf

RTFParser

public abstract class RTFParser extends AbstractFilter
RTFParser is a subclass of AbstractFilter which understands basic RTF syntax and passes a stream of control words, text, and begin/end group indications to its subclass. Normally programmers will only use RTFFilter, a subclass of this class that knows what to do with the tokens this class parses.
see
AbstractFilter
see
RTFFilter

Fields Summary
public int
level
The current RTF group nesting level.
private int
state
private StringBuffer
currentCharacters
private String
pendingKeyword
private int
pendingCharacter
private long
binaryBytesLeft
ByteArrayOutputStream
binaryBuf
private boolean[]
savedSpecials
protected PrintStream
warnings
A stream to which to write warnings and debugging information while parsing. This is set to System.out to log any anomalous information to stdout.
private final int
S_text
private final int
S_backslashed
private final int
S_token
private final int
S_parameter
private final int
S_aftertick
private final int
S_aftertickc
private final int
S_inblob
static final boolean[]
rtfSpecialsTable
Constructors Summary
public RTFParser()

    rtfSpecialsTable = (boolean[])noSpecialsTable.clone();
    rtfSpecialsTable['\n"] = true;
    rtfSpecialsTable['\r"] = true;
    rtfSpecialsTable['{"] = true;
    rtfSpecialsTable['}"] = true;
    rtfSpecialsTable['\\"] = true;
  
    currentCharacters = new StringBuffer();
    state = S_text;
    pendingKeyword = null;
    level = 0;
    //warnings = System.out;

    specialsTable = rtfSpecialsTable;
  
Methods Summary
public abstract voidbegingroup()
Implemented by subclasses to react to an increase in the nesting level.

public voidclose()
Closes the parser. Currently, this simply does a flush(), followed by some minimal consistency checks.

    flush();

    if (state != S_text || level > 0) {
      warning("Truncated RTF file.");
      
      /* TODO: any sane way to handle termination in a non-S_text state? */
      /* probably not */

      /* this will cause subclasses to behave more reasonably
	 some of the time */
      while (level > 0) {
	  endgroup();
	  level --;
      }
    }

    super.close();
  
public abstract voidendgroup()
Implemented by subclasses to react to the end of a group.

public voidflush()
Flushes any buffered but not yet written characters. Subclasses which override this method should call this method before flushing any of their own buffers.

    super.flush();

    if (state == S_text && currentCharacters.length() > 0) {
      handleText(currentCharacters.toString());
      currentCharacters = new StringBuffer();
    }
  
public abstract voidhandleBinaryBlob(byte[] data)
Implemented by subclasses to handle the contents of the \bin keyword.

public abstract booleanhandleKeyword(java.lang.String keyword)
Implemented by subclasses to interpret a parameter-less RTF keyword. The keyword is passed without the leading '/' or any delimiting whitespace.

public abstract booleanhandleKeyword(java.lang.String keyword, int parameter)
Implemented by subclasses to interpret a keyword with a parameter.

param
keyword The keyword, as with handleKeyword(String).
param
parameter The parameter following the keyword.

public abstract voidhandleText(java.lang.String text)
Implemented by subclasses to interpret text from the RTF stream.

public voidhandleText(char ch)

        // in a \bin blob

                           
      
                               
        
             
      
      
   handleText(String.valueOf(ch)); 
protected voidwarning(java.lang.String s)

	if (warnings != null) {
	    warnings.println(s);
	}
    
public voidwrite(java.lang.String s)

    if (state != S_text) {
      int index = 0;
      int length = s.length();
      while(index < length && state != S_text) {
	write(s.charAt(index));
	index ++;
      }
      
      if(index >= length)
	return;

      s = s.substring(index);
    }

    if (currentCharacters.length() > 0)
      currentCharacters.append(s);
    else
      handleText(s);
  
public voidwrite(char ch)

    boolean ok;

    switch (state)
    {
      case S_text:
        if (ch == '\n" || ch == '\r") {
	  break;  // unadorned newlines are ignored
	} else if (ch == '{") {
	  if (currentCharacters.length() > 0) {
	    handleText(currentCharacters.toString());
	    currentCharacters = new StringBuffer();
	  }
	  level ++;
	  begingroup();
	} else if(ch == '}") {
	  if (currentCharacters.length() > 0) {
	    handleText(currentCharacters.toString());
	    currentCharacters = new StringBuffer();
	  }
	  if (level == 0)
	    throw new IOException("Too many close-groups in RTF text");
          endgroup();
	  level --;
	} else if(ch == '\\") {
	  if (currentCharacters.length() > 0) {
	    handleText(currentCharacters.toString());
	    currentCharacters = new StringBuffer();
	  }
	  state = S_backslashed;
	} else {
	  currentCharacters.append(ch);
	}
	break;
      case S_backslashed:
	if (ch == '\'") {
	  state = S_aftertick;
	  break;
	}
	if (!Character.isLetter(ch)) {
	  char newstring[] = new char[1];
	  newstring[0] = ch;
	  if (!handleKeyword(new String(newstring))) {
	    warning("Unknown keyword: " + newstring + " (" + (byte)ch + ")");
	  }
	  state = S_text;
	  pendingKeyword = null;
	  /* currentCharacters is already an empty stringBuffer */
	  break;
	}
	
	state = S_token;
	/* FALL THROUGH */
      case S_token:
	if (Character.isLetter(ch)) {
	  currentCharacters.append(ch);
	} else {
	  pendingKeyword = currentCharacters.toString();
	  currentCharacters = new StringBuffer();
	  
	  // Parameter following?
	  if (Character.isDigit(ch) || (ch == '-")) {
	    state = S_parameter;
	    currentCharacters.append(ch);
	  } else {
	    ok = handleKeyword(pendingKeyword);
	    if (!ok)
	      warning("Unknown keyword: " + pendingKeyword);
	    pendingKeyword = null;
	    state = S_text;

	    // Non-space delimiters get included in the text
	    if (!Character.isWhitespace(ch))
	      write(ch);
	  }
	}
	break;
      case S_parameter:
	if (Character.isDigit(ch)) {
	  currentCharacters.append(ch);
	} else {
	  /* TODO: Test correct behavior of \bin keyword */
	  if (pendingKeyword.equals("bin")) {  /* magic layer-breaking kwd */
	    long parameter = Long.parseLong(currentCharacters.toString());
	    pendingKeyword = null;
	    state = S_inblob;
	    binaryBytesLeft = parameter;
	    if (binaryBytesLeft > Integer.MAX_VALUE)
		binaryBuf = new ByteArrayOutputStream(Integer.MAX_VALUE);
	    else
		binaryBuf = new ByteArrayOutputStream((int)binaryBytesLeft);
	    savedSpecials = specialsTable;
	    specialsTable = allSpecialsTable;
	    break;
	  }
	      
	  int parameter = Integer.parseInt(currentCharacters.toString());
	  ok = handleKeyword(pendingKeyword, parameter);
	  if (!ok)
	    warning("Unknown keyword: " + pendingKeyword +
		    " (param " + currentCharacters + ")");
	  pendingKeyword = null;
	  currentCharacters = new StringBuffer();
	  state = S_text;

	  // Delimiters here are interpreted as text too
	  if (!Character.isWhitespace(ch))
	    write(ch);
	}
	break;
      case S_aftertick:
	if (Character.digit(ch, 16) == -1)
	  state = S_text;
	else {
	  pendingCharacter = Character.digit(ch, 16);
	  state = S_aftertickc;
	}
	break;
      case S_aftertickc:
	state = S_text;
	if (Character.digit(ch, 16) != -1)
	{
	  pendingCharacter = pendingCharacter * 16 + Character.digit(ch, 16);
	  ch = translationTable[pendingCharacter];
	  if (ch != 0)
	      handleText(ch);
	}
	break;
      case S_inblob:
	binaryBuf.write(ch);
	binaryBytesLeft --;
	if (binaryBytesLeft == 0) {
	    state = S_text;
	    specialsTable = savedSpecials;
	    savedSpecials = null;
	    handleBinaryBlob(binaryBuf.toByteArray());
	    binaryBuf = null;
	}
      }
  
public voidwriteSpecial(int b)

    write((char)b);