/*
* Javolution - Java(TM) Solution for Real-Time and Embedded Systems
* Copyright (C) 2005 - Javolution (http://javolution.org/)
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software is
* freely granted, provided that this notice is preserved.
*/
package javolution37.javolution.lang;
import java.lang.CharSequence;
import java.text.ParsePosition;
import javolution37.javolution.realtime.ObjectFactory;
import java.io.IOException;
/**
* This class represents the base format for text parsing and formatting;
* it supports {@link CharSequence} and {@link java.lang.Appendable}
* interfaces for greater flexibility.
*
* The default format (used by valueOf(CharSequence),
* toString() or toText())
* can be {@link javolution37.javolution.realtime.LocalContext locally scoped}.
* For example:[code]
* public class Complex extends RealtimeObject {
* public static final LocalReference> FORMAT
* = new LocalReference>(new TextFormat() {
* ... // Default format (cartesien form).
* });
* public Complex valueOf(CharSequence csq) {
* return FORMAT.get().parse(csq);
* }
* public Text toText() {
* return FORMAT.get().format(this);
* }
* }
* ...
* Matrix M = ...;
* LocalContext.enter();
* try {
* Complex.FORMAT.set(POLAR); // Current thread displays complex numbers
* System.out.prinln(M); // using the polar form.
* } finally {
* LocalContext.exit(); // Current thread reverts to previous format.
* }[/code]
*
* For parsing/formatting of primitive types, the {@link TypeFormat}
* utility class is recommended.
*
* @author Jean-Marie Dautelle
* @version 3.7, January 14, 2006
*/
public abstract class TextFormat {
/*
* Default constructor.
*/
protected TextFormat() {
}
/**
* Formats the specified object into an Appendable
*
* @param obj the object to format.
* @param dest the appendable destination.
* @return the specified Appendable.
* @throws IOException if an I/O exception occurs.
*/
public abstract Appendable format( T obj, Appendable dest)
throws IOException;
/**
* Parses a portion of the specified CharSequence from the
* specified position to produce an object. If parsing succeeds, then the
* index of the cursor argument is updated to the index after
* the last character used.
*
* @param csq the CharSequence to parse.
* @param cursor the cursor holding the current parsing index.
* @return the object parsed from the specified character sub-sequence.
* @throws RuntimeException if any problem occurs while parsing the
* specified character sequence (e.g. illegal syntax).
*/
public abstract T parse(CharSequence csq, Cursor cursor);
/**
* Formats the specified object to a {@link Text} instance
* (convenience method).
*
* @param obj the object being formated.
* @return the text representing the specified object.
*/
public final Text format( T obj) {
try {
TextBuilder tb = TextBuilder.newInstance();
format(obj, tb);
return tb.toText();
} catch (IOException e) {
throw new Error(); // Should never happen.
}
}
/**
* Parses a whole character sequence from the beginning to produce an object
* (convenience method).
*
* @param csq the whole character sequence to parse.
* @return the corresponding object.
* @throws IllegalArgumentException if the specified character sequence
* cannot be fully parsed.
*/
public final T parse(CharSequence csq) {
Cursor cursor = Cursor.newInstance();
try {
T obj = parse(csq, cursor);
if (cursor.getIndex() != csq.length())
throw new IllegalArgumentException("Parsing of " + csq
+ " incomplete (terminated at index: " + cursor.getIndex()
+ ")");
return obj;
} catch (IllegalArgumentException e) {
throw e;
} catch (RuntimeException e) {
throw new IllegalArgumentException("Cannot parse \"" + csq +
"\" (" + e.toString() + ")");
} finally {
cursor.recycle();
}
}
/**
* This class represents a parsing cursor.
*/
public static class Cursor extends ParsePosition {
/**
* Holds the cursor factory.
*/
private static final ObjectFactory FACTORY = new ObjectFactory() {
public Object create() {
return new Cursor();
}
};
/**
* Holds the cursor index.
*/
private int _index;
/**
* Default constructor.
*/
private Cursor() {
super(0);
}
/**
* Returns a new cursor instance (possibly recycled).
*
* @return a cursor instance whose index is 0.
*/
public static Cursor newInstance() {
Cursor cursor = (Cursor) FACTORY.object();
cursor._index = 0;
return cursor;
}
/**
* Returns this cursor index.
*
* @return the index of the next character to parse.
*/
public final int getIndex() {
return _index;
}
/**
* Sets the cursor index.
*
* @param i the index of the next character to parse.
*/
public final void setIndex(int i) {
_index = i;
}
/**
* Indicates if this cursor has not yet reached the end of the specified
* character sequence.
*
* @param csq the character sequence iterated by this cursor.
* @return this.getIndex() < csq.length()
*/
public final boolean hasNext(CharSequence csq) {
return _index < csq.length();
}
/**
* Returns the next character at the cursor position in the specified
* character sequence and increments the cursor position by one.
*
* @param csq the character sequence iterated by this cursor.
* @return the character at the cursor position.
* @throws IndexOutOfBoundsException if (this.getIndex() < 0)
* || (this.getIndex(() >= csq.length())
*/
public final char next(CharSequence csq) {
return csq.charAt(_index++);
}
/**
* Moves this cursor forward until it points to a character
* different from the character specified.
*
* @param c the character to skip.
* @param csq the character sequence iterated by this cursor.
* @return true if this cursor points to a character
* different from the ones specified; false
* otherwise (e.g. end of sequence reached).
*/
public final boolean skip(char c, CharSequence csq) {
int length = csq.length();
while ((_index < length) && (csq.charAt(_index) == c)) {
_index++;
}
return _index < length;
}
/**
* Moves this cursor forward until it points to a character
* different from any of the character in the specified set.
* For example: [code]
* // Reads numbers separated by tabulations or spaces.
* FastTable numbers = new FastTable();
* while (cursor.skip(CharSet.SPACE_OR_TAB, csq)) {
* numbers.add(TypeFormat.parseInt(csq, cursor));
* }[/code]
*
* @param charSet the character to skip.
* @param csq the character sequence iterated by this cursor.
* @return true if this cursor points to a character
* different from the ones specified; false
* otherwise (e.g. end of sequence reached).
*/
public final boolean skip(CharSet charSet, CharSequence csq) {
int length = csq.length();
while ((_index < length) && (charSet.contains(csq.charAt(_index)))) {
_index++;
}
return _index < length;
}
/**
* Increments the cursor index by one.
*/
public final void increment() {
_index++;
}
/**
* Increments the cursor index by the specified value.
*
* @param i the increment value.
*/
public final void increment(int i) {
_index += i;
}
/**
* Recycles this cursor for reuse.
*/
public final void recycle() {
FACTORY.currentPool().recycle(this);
}
}
}
Ads help maintain this website.