|
|||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
java.lang.Objectcox.jmatt.java.MathTools.test.MathTestFormatter
public class MathTestFormatter
This class provides a more sophisticated templating engine than Question.fillTemplate()
. In terms of capability it lies between that method
and Apache Velocity, but far closer to the former than the latter. It uses a Velocity-like syntax but serves mostly as a text-replacement engine with a few
extras.
Mainly because this is included within the MathTools
package and requires no external equipment, no subclassing, no re-invention of a GUI,
and no custom-scripting-application construction. But if you like Velocity there is no reason whatsoever not to use it!
MathTestFormatter
is written specifically to work with the other MathTools
classes. It is a part of the package and it has NO
external dependencies. It is included, it's easy, and it works with the other tools.
Speaking more philosophically, MathTestFormatter
offers a very flexible test-centric way to format and present data. MTest
does
the same thing but it is much more question-centered. This class is designed to represent the test as a whole. While Question
s are an essential
part they are not the entirety of the test.
In addition to dealing with Question instances, this class has ample machinery to handle other aspects of the test. It can load a template String or Properties
from an external source, add any other necessary data, and merge the whole thing. It can row-format a java.sql.ResultSet
generated by
MathDBC
(think grade reports) and, since the templating machinery doesn't use any XML-dangerous characters intrinsically, generate XML output
easily. MathTestFormatter
offers maximum flexibility in producing a print-ready test without needing any middleware.
Finally, there is the abundance of format()
methods. The basic fillTemplate()
method in this class accepts a template String and
a Properties object for data but there is more machinery under the hood. The formatting methods are:
format()
applies the internal data to the internal template.format(String)
applies the internal data to the template supplied.format(java.sql.ResultSet)
applies the internal data to the results of a database query.format(java.util.List)
applies the internal data to a List
of template Strings.format(String[])
applies the internal data to an array of template Strings.format(Properties[], boolean)
applies each Properties object to the internal template. The internal data may also be applied.reFormat(String)
formats the internal template with the capture groups from a regular expression.reFormat(String[])
applies reFormat()
to an array of Strings.The only tradeoff is complexity. MTest
is considerably easier to use than this class. That is not to say using this class is hard, merely
that it is not quite as easy as MTest. If the trade is worth it, use it!
Question.fillTemplate()
...The Question.fillTemplate()
method was designed around the internal structure and formatting of a test question. Its replacement token
syntax is both different and simpler than that used here. This was a deliberate choice meant to separate test question formatting from test
formatting. One is 'internal' in intent; focusing primarily on how to deal with the data before it's ready to go on the test. The other is more 'external'
in that it assumes that, by the time it is used, the internal data handling and formatting is done. The question is ready for the test and only needs to be
placed appropriately.
This class is a more 'real' templating engine in that the syntax is different from Question.fillTemplate()
. The templates here use an
identifier-based token syntax rather than array-position-based. This makes designing the template for a whole test easier and more 'natural'; to wit, the
template 'Chapter Exam ${version} ${date}' is easier to understand than 'Chapter Exam <4> <7>'.
Field Summary | |
---|---|
static java.lang.String |
DEFAULT_ANSWER_KEY
This is the default Answer key format: '@{QID}_Ans'. |
static java.lang.String |
DEFAULT_PROBLEM_KEY
This is the default Problem key format: '@{QID}_Prob'. |
Constructor Summary | |
---|---|
MathTestFormatter()
Standard constructor for scripts. |
Method Summary | |
---|---|
void |
addGlobalPizza()
Add the entire contents of the Global Pizza to the internal Properties object. |
void |
addMap(java.util.Map pMap)
Add a java.util.Map to the properties. |
void |
addProperties(java.util.Properties pNewProps)
Add a Properties object to the internal one. |
void |
addProperties(java.lang.String pData,
java.lang.String pLineSep,
java.lang.String pKVSep)
Add Properties to the internal Properties object. |
void |
addProperty(java.lang.String pKey,
java.lang.String pValue)
Add a single property to the internal Properties object. |
MathTestFormatter |
addQuestion(Question pQuestion)
Add the data from a Question to the internal Properties. |
MathTestFormatter |
click()
Increment the internal clicker by +1. |
void |
dumpDebug()
This is a debugging tool. |
static java.lang.String |
fillTemplate(java.lang.String pTemplate,
java.util.Properties pData)
This is the foundation method for the entire class. |
java.lang.String |
format()
Format the internal template String using the internal Properties data. |
java.util.List<java.lang.String> |
format(java.util.List<java.lang.String> pTemplates)
This method formats a java.util.List of template Strings into a List of filled templates using the same internal data for all. |
java.lang.String[] |
format(java.util.Properties[] pProps,
boolean pInternal)
Use the internal template String for an array of Properties objects, with or without the internal data. |
java.lang.String |
format(java.sql.ResultSet pResults)
This method formats each row in a java.sql.ResultSet . |
java.lang.String |
format(java.lang.String pTemplate)
Format the supplied template String using the internal data. |
java.lang.String[] |
format(java.lang.String[] pTemplates)
This method operates exactly per the format(List) method except that it operates on a String[] array and the returned values may be null. |
void |
loadProperties(java.lang.String pFileName,
boolean pGRAS)
Load a Properties object from a file and add it to the currently-set internal properties. |
void |
loadTemplate(java.lang.String pFileName,
boolean pGRAS)
Load a template String from a file or resource. |
java.lang.String |
reFormat(java.lang.String pData)
This is the basic regular-expression formatting method. |
java.lang.String[] |
reFormat(java.lang.String[] pData)
This method applies reFormat() to a String[] array. |
void |
reset()
Reset the internal template and Properties object to null. |
void |
setClicker(long pClickerValue)
As does Question , this class maintains an internal long usable to index the key values for Questions added to the internal data. |
static void |
setDefaultNewline(char pNewline)
This method sets the character sequence used for the @{newline} field. |
static void |
setDefaultTimestamp(java.lang.String pFormat)
Set the default format for the @{timestamp} field. |
void |
setMathClassLoader(boolean pEnable)
Enable or disable the Math ClassLoader for loading properties files or templates. |
static void |
setNewDefaultNewline(char pNewline)
Instance version. |
void |
setNewDefaultTimestamp(java.lang.String pFormat)
Instance version. |
void |
setQuestionKeyFormat(java.lang.String pProbFormat,
java.lang.String pAnsFormat)
This method sets the key String format used when adding a Question to the internal Properties. |
boolean |
setRegex(java.lang.String pRegex)
Use this method to set a regular expression for the reFormat() methods. |
boolean |
setRegex(java.lang.String pRegex,
boolean ynLiteral,
boolean ynCanonEq)
Use this method to set a regular expression AND the two flags that cannot be set via embedded flag expressions. |
void |
setTemplate(java.lang.String pTemplate)
Set (or clear) the internal template String. |
java.lang.String |
toString()
Overriden to call format() . |
Methods inherited from class java.lang.Object |
---|
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait |
Field Detail |
---|
public static final java.lang.String DEFAULT_PROBLEM_KEY
public static final java.lang.String DEFAULT_ANSWER_KEY
Constructor Detail |
---|
public MathTestFormatter()
Method Detail |
---|
public static final java.lang.String fillTemplate(java.lang.String pTemplate, java.util.Properties pData)
This is the foundation method for the entire class. It does the actual work of filling out a template. It accepts as input a String template containing
replacement tokens and a java.util.Properties
object containing the values for the tokens. If either is null or empty the template String is
returned unaltered. Any errors are reported at Error level.
Standard replacement tokens are of the form '${someKeyName}' where someKeyName is a key within the Properties
object. When
invoked this method replaces the entire replacement token with the value of the key between the curly braces. For example if the template is 'I like ${food}!'
and the Properties contained the mapping 'food = gyros' the completed template would be 'I like gyros!'.
NOTE: This method makes excessive use of String replaceAll()
which uses regular expressions for key-matching. This can cause problems for
key-value mappings whose key contains forbidden characters. Since the dot is common it and it alone is escaped. Using a dot in a property key will work, no
other collision characters will! Use with care.
In addition to the data fields defined by the Properties object, MathTestFormatter
defines a group of special tokens (or fields). These are
handled internally and automatically, though some can be configured. The special field tokens are:
DateFormat.SHORT)
.The @{timestamp} field is configured via the setDefaultTimestamp()
methods. The formatting String is per the SimpleDateFormat
class; it might not be available. If not the hard-wired timestamp format is DateFormat.FULL
. The @{newline} field can be configured through the
setDefaultNewline()
methods. The only values accepted are newline ('/n'), carriage return ('/r'), or both ('/r/n'). Once set these parameters
remain in effect until they are changed. Text within the comment container ('#{' to '}#') does not appear when the template is processed. Also note that
unlike other tokens the comment delimiter ('#') appears on both sides of the curly braces.
The '@{blank}' token is replaced with a single space character. This token is replaced after all other tokens have been handled, whether regular, special, or numeric. This allows templates to be 'blanked out' if desired by setting a the value of one or more keys in the Properties object to '@{blank}'. First the standard token is replaced with the value '@{blank}' which is then replaced with a single space. For those interested, the order in which tokens are replaced is:
Properties
object 'pData'.ResultSet
fields or regex capture groups.Token order replacement should not be relevant other than for blanking, but occasional edge conditions may make it relevant.
Any errors occurring during template merging are reported at Error level. The process halts at that point.
pTemplate
- A String template containing one or more replacement tokens.pData
- A Properties object whose keys match the replacement characters and whose mapped values will replace their template tokens.
public static void setDefaultTimestamp(java.lang.String pFormat)
SimpleDateFormat
and may not work. If not, the timestamp is formatted per
DateFormat.FULL
for both date and time.
pFormat
- The formatting String for the timestamp field.public void setNewDefaultTimestamp(java.lang.String pFormat)
public static void setDefaultNewline(char pNewline)
This method sets the character sequence used for the @{newline} field. Since there are only three choices they are represented by char:
Upper-case letters also work. Any character other than the ones listed are silently ignored.
public static void setNewDefaultNewline(char pNewline)
public void reset()
public void setTemplate(java.lang.String pTemplate)
public void addProperties(java.util.Properties pNewProps)
public void addProperties(java.lang.String pData, java.lang.String pLineSep, java.lang.String pKVSep)
pData
- The String containing the properties data to be added.pLineSep
- The properties (line) separator to be used. Defaults to a newline.pKVSep
- The String used to separate the key from the value. Defaults to an equals sign.public void addGlobalPizza()
public void addProperty(java.lang.String pKey, java.lang.String pValue)
pKey
- The key used to store or remove an internal mapping.pValue
- The value to store under pKey, or null to remove any stored value.public void addMap(java.util.Map pMap)
java.util.Map
to the properties. This method uses the widest cast on the Map
interface to allow for maximum flexibility.
Null or invalid keys or values are silently ignored.
pMap
- The Map to be added to the internal properties.public void setMathClassLoader(boolean pEnable)
pEnable
- true to use CapCom's ClassLoader, false not to.public void loadProperties(java.lang.String pFileName, boolean pGRAS)
pFileName
- The name of the file to load.pGRAS
- true to getResourceAsStream()
, false to open as a standard File
.public void loadTemplate(java.lang.String pFileName, boolean pGRAS)
loadProperties()
as is error reporting.
public java.lang.String format()
public java.lang.String format(java.lang.String pTemplate)
public java.lang.String format(java.sql.ResultSet pResults)
This method formats each row in a java.sql.ResultSet
. The internal Properties object is available as are all the automatic fields. The column
data from the ResultSet
is accessed via a special set of fields defined in this method: '${X}' where X is the number of the column
containing the data. Since SQL indexing is one-based the first data value is '${1}' for column 1, etc. The '${0}' token is the row count which starts
at one and moves up by one with each row. The '@{count}' token is used here: it starts at zero and moves up by one.
In use, this method calls the ResultSet getObject()
method to retrieve the data then casts it as String, or blank if the value is null. Each
row in the ResultSet
is processed and the result for each appended to a StringBuffer
, whose String value is then returned. There
is no end-of-line processing done so the template String should include an embedded newline or the ${newline} field.
Any errors are reported at Error level and the first such will terminate the entire process. Use with care. If pResult is null or closed the method returns null and nothing else happens.
pResults
- The ResultSet
to be processed.public java.util.List<java.lang.String> format(java.util.List<java.lang.String> pTemplates)
This method formats a java.util.List
of template Strings into a List
of filled templates using the same internal data for all.
If any template is null or blank the returned String will be empty but not null. If the List
sent in is null or empty an empty (but non-null)
List
is returned.
The '@{count}' token is used here. It holds the (zero-based) number of the current template. That is, the first template is '0', the
second is '1', etc. NOTE: '@{count}' is available to the other format()
methods but its value will always be zero!
pTemplates
- A List<String>
of templates.
List<String>
resulting from filling pTemplates with the current data.public java.lang.String[] format(java.lang.String[] pTemplates)
format(List)
method except that it operates on a String[] array and the returned values may be null. The
'@{count} token equals the array index of the current template String. If the array sent in is null or empty an empty array is returned.
public java.lang.String[] format(java.util.Properties[] pProps, boolean pInternal)
Use the internal template String for an array of Properties objects, with or without the internal data. If any element in the Properties[] array is null or empty a blank (but not null) String is returned for that entry. The returned array will be one element longer than the one sent in: element zero is the result from the internal data or blank if it is not used. If the array sent in is null or empty an empty array is returned. If the internal template is null or empty the result is an empty array.
The '@{count}' token for this method equals the index of the result array. That is, element 0 is the result of the internal data or blank, element 1 is from the first (element 0) Properties object in the array sent in, etc.
pProps
- The Properties[] array to apply to the internal template, in order.pInternal
- true to include the internal data, false to exclude it.
public boolean setRegex(java.lang.String pRegex)
reFormat()
methods. When set the regex is compiled into a Pattern
which is
stored internally. If the regex cannot be compiled it is reported at Debug level and no action is taken.
pRegex
- The regular expression to compile. If null or blank false is returned immediately.
public boolean setRegex(java.lang.String pRegex, boolean ynLiteral, boolean ynCanonEq)
pRegex
- The regex to compile into a Pattern.ynLiteral
- true to set the Pattern.LITERAL
flag, false to leave it out.ynCanonEq
- true to set Pattern.CANON_EQ
, false not to.public java.lang.String reFormat(java.lang.String pData)
This is the basic regular-expression formatting method. It uses the previously-set regex, applies it to the data String sent in, and formats the internal template against it. It is tacitly assumed that the regex contains one or more capture groups. These groups are accessed by the '${N}' fields where the number N is the (one-based) number of the capture group. The '${0}' field matches the entire data String. The replacement fields are guaranteed to be non-null; if a capture group did not match its (null) value becomes a blank String.
NOTE: All internal properties as well as the special fields are also available. If the regex does not match the only valid field will be '${0}'. If pData
is null or empty '' is returned. If no regex is set the return value is the same as format()
; the '${0}' field will not be replaced.
pData
- The data String to match against the internal regex.
public java.lang.String[] reFormat(java.lang.String[] pData)
reFormat()
to a String[] array. This is essentially the same method but the '@{count}' field is also available; it holds
the index of the element being formatted. If the array sent in is null or empty an empty array is returned. The elements of the array returned may be blank
but will never be null. Each String is trimmed before it is sent in.
pData
- The String[] array to apply the reFormat()
method to.
public void setQuestionKeyFormat(java.lang.String pProbFormat, java.lang.String pAnsFormat)
This method sets the key String format used when adding a Question
to the internal Properties. Each format is a template String but these are
considerably simpler than the ones available to the Question
class. Each template has exactly two special fields available to it:
Question
's ID.Each format has a default value. Setting either to null or blank resets it to its default value. CRITICAL NOTE: These formats must be set properly or the data will not be accessible from the template!
There are two ways to handle Question
formatting. The first way is to set the default format for the Question
class before
generating any instances and using its Clicker. Alternately the ID may be set explicitly for each instance. This places the responsibility for proper
identification on the Question
but, since two elements are added to the internal pizza for each Question
added. The default key
formats handle this by appending '_Prob' or '_Ans' to the Question's ID. This is the best procedure if the Question IDs are descriptive in nature.
The second way to handle things is to ignore the Question's ID and handle everything internally here. Set the formats to something like
'Q@{INDX}_P' and 'Q@{INDX}_A' and use the internal Clicker. When using this method the Clicker MUST be initialized to the first number used on the
template and the click()
method MUST be called (on MathTestFormatter
and NOT the Question
!!!) after each Question
is added.
pProbFormat
- The template String used to format the key under which the Problem is added.pAnsFormat
- The template used to format the key for the Answer.public void setClicker(long pClickerValue)
Question
, this class maintains an internal long usable to index the key values for Questions added to the internal data. Use this method
to set it. Any value less than zero becomes zero.
pClickerValue
- The new value for the internal index clicker.public MathTestFormatter click()
public MathTestFormatter addQuestion(Question pQuestion)
Add the data from a Question
to the internal Properties. The Problem and Answer components are each added under their respective keys, set
using the setQuestionKeyFormat()
method, and the @{QID} and @{INDX} fields will be set appropriately. A self-reference is
returned to allow method chaining. If the pQuestion is null this method returns silently. If the Problem or Answer is null or blank it is not added.
The Question
data is extracted via formatProblem()
and formatAnswer()
so it is critical to make sure the formats
cooperate with each other. The best thing to do is set the Question ID format to identify the Question (as it should) and nothing else. Set the
Problem and Answer formats to '<1>' and '<2>' respectively and don't worry about key-value-formatting. Include the '@{QID}' token when setting
Problem- and Answer-key formats here.
pQuestion
- The Question
to be added to the internal data.
public void dumpDebug()
public java.lang.String toString()
format()
. If nothing is set a default String is printed.
toString
in class java.lang.Object
|
|||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |