| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| /* |
| * $Id$ |
| */ |
| |
| /* |
| * |
| * XMLFileLogger.java |
| * |
| */ |
| package org.apache.qetest; |
| |
| import java.io.BufferedWriter; |
| import java.io.File; |
| import java.io.FileWriter; |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.util.Date; |
| import java.util.Enumeration; |
| import java.util.Hashtable; |
| import java.util.Properties; |
| |
| /** |
| * Logger that saves output to a simple XML-format file. |
| * @todo improve escapeString so it's more rigorous about escaping |
| * @author Shane_Curcuru@lotus.com |
| * @version $Id$ |
| */ |
| public class XMLFileLogger implements Logger |
| { |
| |
| //----------------------------------------------------- |
| //-------- Constants for results file structure -------- |
| //----------------------------------------------------- |
| |
| /** XML root element tag: root of our output document. */ |
| public static final String ELEM_RESULTSFILE = "resultsfile"; |
| |
| /** XML element tag: testFileInit() parent element. */ |
| public static final String ELEM_TESTFILE = "testfile"; |
| |
| /** XML element tag: testFileClose() leaf element |
| * emitted immediately before closing ELEM_TESTFILE. |
| */ |
| public static final String ELEM_FILERESULT = "fileresult"; |
| |
| /** XML element tag: testCaseInit() parent element. */ |
| public static final String ELEM_TESTCASE = "testcase"; |
| |
| /** XML element tag: testCaseClose() leaf element |
| * emitted immediately before closing ELEM_TESTCASE. |
| */ |
| public static final String ELEM_CASERESULT = "caseresult"; |
| |
| /** XML element tag: check*() element. */ |
| public static final String ELEM_CHECKRESULT = "checkresult"; |
| |
| /** XML element tag: logStatistic() element. */ |
| public static final String ELEM_STATISTIC = "statistic"; |
| |
| /** XML element tag: logStatistic() child element. */ |
| public static final String ELEM_LONGVAL = "longval"; |
| |
| /** XML element tag: logStatistic() child element. */ |
| public static final String ELEM_DOUBLEVAL = "doubleval"; |
| |
| /** XML element tag: log*Msg() element. */ |
| public static final String ELEM_MESSAGE = "message"; |
| |
| /** XML element tag: logArbitrary() element. */ |
| public static final String ELEM_ARBITRARY = "arbitrary"; |
| |
| /** XML element tag: logHashtable() parent element. */ |
| public static final String ELEM_HASHTABLE = "hashtable"; |
| |
| /** XML element tag: logHashtable() child element. */ |
| public static final String ELEM_HASHITEM = "hashitem"; |
| |
| /** XML attribute tag: log*Msg(), etc. attribute denoting |
| * loggingLevel for that element. |
| */ |
| public static final String ATTR_LEVEL = "level"; |
| |
| /** XML attribute tag: comment value for various methods. */ |
| public static final String ATTR_DESC = "desc"; |
| |
| /** XML attribute tag: Date.toString() attribute on |
| * ELEM_TESTFILE and ELEM_FILERESULT. */ |
| public static final String ATTR_TIME = "time"; |
| |
| /** XML attribute tag: PASS|FAIL|etc. attribute on |
| * ELEM_CHECKRESULT, ELEM_CASERESULT, ELEM_FILERESULT. |
| */ |
| public static final String ATTR_RESULT = "result"; |
| |
| /** XML attribute tag: key name for ELEM_HASHITEM. */ |
| public static final String ATTR_KEY = "key"; |
| |
| /** XML attribute tag: actual output filename on ELEM_RESULTSFILE. */ |
| public static final String ATTR_FILENAME = OPT_LOGFILE; |
| |
| /** XML attribute tag: test filename on ELEM_TESTFILE. */ |
| public static final String ATTR_TESTFILENAME = "filename"; |
| |
| /** Parameter: if we should flush on every testCaseClose(). */ |
| public static final String OPT_FLUSHONCASECLOSE = "flushOnCaseClose"; |
| |
| //----------------------------------------------------- |
| //-------- Class members and accessors -------- |
| //----------------------------------------------------- |
| |
| /** If we're ready to start outputting yet. */ |
| protected boolean ready = false; |
| |
| /** If an error has occoured in this Logger. */ |
| protected boolean error = false; |
| |
| /** If we should flush on every testCaseClose(). */ |
| protected boolean flushOnCaseClose = true; |
| |
| /** |
| * Accessor for flushing; is set from properties. |
| * |
| * @return current flushing status |
| */ |
| public boolean getFlushOnCaseClose() |
| { |
| return (flushOnCaseClose); |
| } |
| |
| /** |
| * Accessor for flushing; is set from properties. |
| * |
| * @param b If we should flush on every testCaseClose() |
| */ |
| public void setFlushOnCaseClose(boolean b) |
| { |
| flushOnCaseClose = b; |
| } |
| |
| /** If we have output anything yet. */ |
| protected boolean anyOutput = false; |
| |
| /** Name of the file we're outputing to. */ |
| protected String fileName = null; |
| |
| /** File reference and other internal convenience variables. */ |
| protected File reportFile; |
| |
| /** File reference and other internal convenience variables. */ |
| protected FileWriter reportWriter; |
| |
| /** File reference and other internal convenience variables. */ |
| protected PrintWriter reportPrinter; |
| |
| /** Generic properties for this logger; sort-of replaces instance variables. */ |
| protected Properties loggerProps = null; |
| |
| //----------------------------------------------------- |
| //-------- Control and utility routines -------- |
| //----------------------------------------------------- |
| |
| /** Simple constructor, does not perform initialization. */ |
| public XMLFileLogger() |
| { /* no-op */ |
| } |
| |
| /** |
| * Constructor calls initialize(p). |
| * @param p Properties block to initialize us with. |
| */ |
| public XMLFileLogger(Properties p) |
| { |
| ready = initialize(p); |
| } |
| |
| /** |
| * Return a description of what this Logger does. |
| * @return "reports results in XML to specified fileName". |
| */ |
| public String getDescription() |
| { |
| return ("org.apache.qetest.XMLFileLogger - reports results in XML to specified fileName."); |
| } |
| |
| /** |
| * Returns information about the Property name=value pairs |
| * that are understood by this Logger: fileName=filename. |
| * @return same as {@link java.applet.Applet.getParameterInfo}. |
| */ |
| public String[][] getParameterInfo() |
| { |
| |
| String pinfo[][] = |
| { |
| { OPT_LOGFILE, "String", |
| "Name of file to use for output; required" }, |
| { OPT_FLUSHONCASECLOSE, "boolean", |
| "if we should flush on every testCaseClose(); optional; default:true" } |
| }; |
| |
| return pinfo; |
| } |
| |
| /** |
| * Accessor methods for our properties block. |
| * |
| * @return our current properties; may be null |
| */ |
| public Properties getProperties() |
| { |
| return loggerProps; |
| } |
| |
| /** |
| * Accessor methods for our properties block. |
| * @param p Properties to set (is cloned). |
| */ |
| public void setProperties(Properties p) |
| { |
| |
| if (p != null) |
| { |
| loggerProps = (Properties) p.clone(); |
| } |
| } |
| |
| /** |
| * Initialize this XMLFileLogger. |
| * Must be called before attempting to log anything. |
| * Opens a FileWriter for our output, and logs Record format: |
| * <pre><resultfile fileName="<i>name of result file</i>"></pre> |
| * |
| * If no name provided, supplies a default one in current dir. |
| * |
| * @param p Properties block to initialize from |
| * @return true if we think we initialized OK |
| */ |
| public boolean initialize(Properties p) |
| { |
| |
| setProperties(p); |
| |
| fileName = loggerProps.getProperty(OPT_LOGFILE, fileName); |
| |
| if ((fileName == null) || fileName.equals("")) |
| { |
| // Make up a file name |
| fileName = "XMLFileLogger-default-results.xml"; |
| loggerProps.put(OPT_LOGFILE, fileName); |
| } |
| |
| // Create a file and ensure it has a place to live; be sure |
| // to insist on an absolute path for later parent path creation |
| reportFile = new File(fileName); |
| try |
| { |
| reportFile = new File(reportFile.getCanonicalPath()); |
| } |
| catch (IOException ioe1) |
| { |
| reportFile = new File(reportFile.getAbsolutePath()); |
| } |
| |
| // Note: bare filenames may not have parents, so catch and ignore exceptions |
| try |
| { |
| File parent = new File(reportFile.getParent()); |
| if ((!parent.mkdirs()) && (!parent.exists())) |
| { |
| |
| // Couldn't create or find the directory for the file to live in, so bail |
| error = true; |
| ready = false; |
| |
| System.err.println( |
| "XMLFileLogger.initialize() WARNING: cannot create directories: " |
| + fileName); |
| |
| // Don't return yet: see if the reportWriter can still create the file later |
| // return(false); |
| } |
| } |
| catch (Exception e) |
| { |
| |
| // No-op: ignore if the parent's not there; trust that the file will get created later |
| } |
| |
| try |
| { |
| reportWriter = new FileWriter(reportFile); |
| } |
| catch (IOException e) |
| { |
| System.err.println("XMLFileLogger.initialize() EXCEPTION: " |
| + e.toString()); |
| e.printStackTrace(); |
| |
| error = true; |
| ready = false; |
| |
| return false; |
| } |
| |
| String tmp = loggerProps.getProperty(OPT_FLUSHONCASECLOSE); |
| if (null != tmp) |
| { |
| setFlushOnCaseClose((Boolean.valueOf(tmp)).booleanValue()); |
| } |
| |
| // Use BufferedWriter for better general performance |
| reportPrinter = new PrintWriter(new BufferedWriter(reportWriter)); |
| ready = true; |
| |
| return startResultsFile(); |
| } |
| |
| /** |
| * Is this Logger ready to log results? |
| * @return status - true if it's ready to report, false otherwise |
| */ |
| public boolean isReady() |
| { |
| |
| // Ensure our underlying logger, if one, is still OK |
| if ((reportPrinter != null) && reportPrinter.checkError()) |
| { |
| |
| // NEEDSWORK: should we set ready = false in this case? |
| // errors in the PrintStream are not necessarily fatal |
| error = true; |
| ready = false; |
| } |
| |
| return ready; |
| } |
| |
| /** Flush this logger - ensure our File is flushed. */ |
| public void flush() |
| { |
| |
| if (isReady()) |
| { |
| reportPrinter.flush(); |
| } |
| } |
| |
| /** |
| * Close this logger - ensure our File, etc. are closed. |
| * Record format: |
| * <pre></resultfile></pre> |
| */ |
| public void close() |
| { |
| |
| flush(); |
| |
| if (isReady()) |
| { |
| closeResultsFile(); |
| reportPrinter.close(); |
| } |
| |
| ready = false; |
| } |
| |
| /** |
| * worker method to dump the xml header and open the resultsfile element. |
| * |
| * @return true if ready/OK, false if not ready (meaning we may |
| * not have output anything!) |
| */ |
| protected boolean startResultsFile() |
| { |
| |
| if (isReady()) |
| { |
| |
| // Write out XML header and root test result element |
| reportPrinter.println("<?xml version=\"1.0\"?>"); |
| |
| // Note: this tag is closed in our .close() method, which the caller had better call! |
| reportPrinter.println("<" + ELEM_RESULTSFILE + " " |
| + ATTR_FILENAME + "=\"" + fileName + "\">"); |
| |
| return true; |
| } |
| else |
| return false; |
| } |
| |
| /** |
| * worker method to close the resultsfile element. |
| * |
| * @return true if ready/OK, false if not ready (meaning we may |
| * not have output a closing tag!) |
| */ |
| protected boolean closeResultsFile() |
| { |
| |
| if (isReady()) |
| { |
| reportPrinter.println("</" + ELEM_RESULTSFILE + ">"); |
| |
| return true; |
| } |
| else |
| return false; |
| } |
| |
| //----------------------------------------------------- |
| //-------- Testfile / Testcase start and stop routines -------- |
| //----------------------------------------------------- |
| |
| /** |
| * Report that a testfile has started. |
| * Begins a testfile element. Record format: |
| * <pre><testfile desc="<i>test description</i>" time="<i>timestamp</i>"></pre> |
| * @param name file name or tag specifying the test. |
| * @param comment comment about the test. |
| */ |
| public void testFileInit(String name, String comment) |
| { |
| |
| if (isReady()) |
| { |
| reportPrinter.println("<" + ELEM_TESTFILE + " " |
| + ATTR_TESTFILENAME + "=\"" |
| + escapeString(name) + "\" " |
| + ATTR_DESC + "=\"" |
| + escapeString(comment) + "\" " |
| + ATTR_TIME + "=\"" |
| + (new Date()).toString() + "\">"); |
| } |
| } |
| |
| /** |
| * Report that a testfile has finished, and report it's result; flushes output. |
| * Ends a testfile element. Record format: |
| * <pre><fileresult desc="<i>test description</i>" result="<i>pass/fail status</i>" time="<i>timestamp</i>"> |
| * </testfile></pre> |
| * @param msg message to log out |
| * @param result result of testfile |
| */ |
| public void testFileClose(String msg, String result) |
| { |
| |
| if (isReady()) |
| { |
| reportPrinter.println("<" + ELEM_FILERESULT + " " + ATTR_DESC |
| + "=\"" + escapeString(msg) + "\" " |
| + ATTR_RESULT + "=\"" + result + "\" " |
| + ATTR_TIME + "=\"" |
| + (new Date()).toString() + "\"/>"); |
| reportPrinter.println("</" + ELEM_TESTFILE + ">"); |
| } |
| |
| flush(); |
| } |
| |
| /** Optimization: for heavy use methods, form pre-defined constants to save on string concatenation. */ |
| private static final String TESTCASEINIT_HDR = "<" + ELEM_TESTCASE + " " |
| + ATTR_DESC + "=\""; |
| |
| /** |
| * Report that a testcase has begun. |
| * Begins a testcase element. Record format: |
| * <pre><testcase desc="<i>case description</i>"></pre> |
| * |
| * @param comment message to log out |
| */ |
| public void testCaseInit(String comment) |
| { |
| |
| if (isReady()) |
| { |
| reportPrinter.println(TESTCASEINIT_HDR + escapeString(comment) |
| + "\">"); |
| } |
| } |
| |
| /** NEEDSDOC Field TESTCASECLOSE_HDR */ |
| private static final String TESTCASECLOSE_HDR = "<" + ELEM_CASERESULT |
| + " " + ATTR_DESC |
| + "=\""; |
| |
| /** |
| * Report that a testcase has finished, and report it's result. |
| * Optionally flushes output. Ends a testcase element. Record format: |
| * <pre><caseresult desc="<i>case description</i>" result="<i>pass/fail status</i>"> |
| * </testcase></pre> |
| * @param msg message of name of test case to log out |
| * @param result result of testfile |
| */ |
| public void testCaseClose(String msg, String result) |
| { |
| |
| if (isReady()) |
| { |
| reportPrinter.println(TESTCASECLOSE_HDR + escapeString(msg) |
| + "\" " + ATTR_RESULT + "=\"" + result |
| + "\"/>"); |
| reportPrinter.println("</" + ELEM_TESTCASE + ">"); |
| } |
| |
| if (getFlushOnCaseClose()) |
| flush(); |
| } |
| |
| //----------------------------------------------------- |
| //-------- Test results logging routines -------- |
| //----------------------------------------------------- |
| |
| /** NEEDSDOC Field MESSAGE_HDR */ |
| private static final String MESSAGE_HDR = "<" + ELEM_MESSAGE + " " |
| + ATTR_LEVEL + "=\""; |
| |
| /** |
| * Report a comment to result file with specified severity. |
| * Record format: <pre><message level="##">msg</message></pre> |
| * @param level severity or class of message. |
| * @param msg comment to log out. |
| */ |
| public void logMsg(int level, String msg) |
| { |
| |
| if (isReady()) |
| { |
| reportPrinter.print(MESSAGE_HDR + level + "\">"); |
| reportPrinter.print(escapeString(msg)); |
| reportPrinter.println("</" + ELEM_MESSAGE + ">"); |
| } |
| } |
| |
| /** NEEDSDOC Field ARBITRARY_HDR */ |
| private static final String ARBITRARY_HDR = "<" + ELEM_ARBITRARY + " " |
| + ATTR_LEVEL + "=\""; |
| |
| /** |
| * Report an arbitrary String to result file with specified severity. |
| * Appends and prepends \\n newline characters at the start and |
| * end of the message to separate it from the tags. |
| * Record format: <pre><arbitrary level="##"><![CDATA[ |
| * msg |
| * ]]></arbitrary></pre> |
| * |
| * Note that arbitrary messages are always wrapped in CDATA |
| * sections to ensure that any non-valid XML is wrapped. This needs |
| * to be investigated for other elements as well (i.e. we should set a |
| * standard for what Logger calls must be well-formed or not). |
| * @param level severity or class of message. |
| * @param msg arbitrary String to log out. |
| * @todo investigate <b>not</b> fully escaping this string, since |
| * it does get wrappered in CDATA |
| */ |
| public void logArbitrary(int level, String msg) |
| { |
| |
| if (isReady()) |
| { |
| reportPrinter.println(ARBITRARY_HDR + level + "\"><![CDATA["); |
| reportPrinter.println(escapeString(msg)); |
| reportPrinter.println("]]></" + ELEM_ARBITRARY + ">"); |
| } |
| } |
| |
| /** NEEDSDOC Field STATISTIC_HDR */ |
| private static final String STATISTIC_HDR = "<" + ELEM_STATISTIC + " " |
| + ATTR_LEVEL + "=\""; |
| |
| /** |
| * Logs out statistics to result file with specified severity. |
| * Record format: <pre><statistic level="##" desc="msg"><longval>1234</longval><doubleval>1.234</doubleval></statistic></pre> |
| * @param level severity of message. |
| * @param lVal statistic in long format. |
| * @param dVal statistic in double format. |
| * @param msg comment to log out. |
| */ |
| public void logStatistic(int level, long lVal, double dVal, String msg) |
| { |
| |
| if (isReady()) |
| { |
| reportPrinter.print(STATISTIC_HDR + level + "\" " + ATTR_DESC |
| + "=\"" + escapeString(msg) + "\">"); |
| reportPrinter.print("<" + ELEM_LONGVAL + ">" + lVal + "</" |
| + ELEM_LONGVAL + ">"); |
| reportPrinter.print("<" + ELEM_DOUBLEVAL + ">" + dVal + "</" |
| + ELEM_DOUBLEVAL + ">"); |
| reportPrinter.println("</" + ELEM_STATISTIC + ">"); |
| } |
| } |
| |
| /** |
| * Logs out Throwable.toString() and a stack trace of the |
| * Throwable with the specified severity. |
| * <p>This uses logArbitrary to log out your msg - message, |
| * a newline, throwable.toString(), a newline, |
| * and then throwable.printStackTrace().</p> |
| * //@todo future work to use logElement() to output |
| * a specific <throwable> element instead. |
| * @author Shane_Curcuru@lotus.com |
| * @param level severity of message. |
| * @param throwable throwable/exception to log out. |
| * @param msg description of the throwable. |
| */ |
| public void logThrowable(int level, Throwable throwable, String msg) |
| { |
| if (isReady()) |
| { |
| StringWriter sWriter = new StringWriter(); |
| |
| sWriter.write(msg + "\n"); |
| sWriter.write(throwable.toString() + "\n"); |
| |
| PrintWriter pWriter = new PrintWriter(sWriter); |
| |
| throwable.printStackTrace(pWriter); |
| logArbitrary(level, sWriter.toString()); |
| } |
| } |
| |
| /** |
| * Logs out a element to results with specified severity. |
| * Uses user-supplied element name and attribute list. Currently |
| * attribute values and msg are forced .toString(). Also, |
| * 'level' is forced to be the first attribute of the element. |
| * Record format: |
| * <pre><<i>element_text</i> level="##" |
| * attribute1="value1" |
| * attribute2="value2" |
| * attribute<i>n</i>="value<i>n</i>"> |
| * msg |
| * </<i>element_text</i>></pre> |
| * @author Shane_Curcuru@lotus.com |
| * @param level severity of message. |
| * @param element name of enclosing element |
| * @param attrs hash of name=value attributes; note that the |
| * caller must ensure they're legal XML |
| * @param msg Object to log out .toString(); caller should |
| * ensure it's legal XML (no CDATA is supplied) |
| */ |
| public void logElement(int level, String element, Hashtable attrs, |
| Object msg) |
| { |
| |
| if (isReady() |
| && (element != null) |
| && (attrs != null) |
| ) |
| { |
| reportPrinter.println("<" + element + " " + ATTR_LEVEL + "=\"" |
| + level + "\""); |
| |
| for (Enumeration keys = attrs.keys(); |
| keys.hasMoreElements(); /* no increment portion */ ) |
| { |
| Object key = keys.nextElement(); |
| |
| reportPrinter.println(key.toString() + "=\"" |
| + escapeString(attrs.get(key).toString()) + "\""); |
| } |
| |
| reportPrinter.println(">"); |
| if (msg != null) |
| reportPrinter.println(msg.toString()); |
| reportPrinter.println("</" + element + ">"); |
| } |
| } |
| |
| /** NEEDSDOC Field HASHTABLE_HDR */ |
| private static final String HASHTABLE_HDR = "<" + ELEM_HASHTABLE + " " |
| + ATTR_LEVEL + "=\""; |
| |
| // Note the HASHITEM_HDR indent; must be updated if we ever switch to another indenting method. |
| |
| /** NEEDSDOC Field HASHITEM_HDR */ |
| private static final String HASHITEM_HDR = " <" + ELEM_HASHITEM + " " |
| + ATTR_KEY + "=\""; |
| |
| /** |
| * Logs out contents of a Hashtable with specified severity. |
| * Indents each hashitem within the table. |
| * Record format: <pre><hashtable level="##" desc="msg"/> |
| * <hashitem key="key1">value1</hashitem> |
| * <hashitem key="key2">value2</hashitem> |
| * </hashtable></pre> |
| * |
| * @param level severity or class of message. |
| * @param hash Hashtable to log the contents of. |
| * @param msg decription of the Hashtable. |
| */ |
| public void logHashtable(int level, Hashtable hash, String msg) |
| { |
| |
| if (isReady()) |
| { |
| reportPrinter.println(HASHTABLE_HDR + level + "\" " + ATTR_DESC |
| + "=\"" + escapeString(msg) + "\">"); |
| |
| if (hash == null) |
| { |
| reportPrinter.print("<" + ELEM_HASHITEM + " " + ATTR_KEY |
| + "=\"null\">"); |
| reportPrinter.println("</" + ELEM_HASHITEM + ">"); |
| } |
| |
| try |
| { |
| for (Enumeration keys = hash.keys(); |
| keys.hasMoreElements(); /* no increment portion */ ) |
| { |
| Object key = keys.nextElement(); |
| |
| // Ensure we'll have clean output by pre-fetching value before outputting anything |
| String value = escapeString(hash.get(key).toString()); |
| |
| reportPrinter.print(HASHITEM_HDR + escapeString(key.toString()) |
| + "\">"); |
| reportPrinter.print(value); |
| reportPrinter.println("</" + ELEM_HASHITEM + ">"); |
| } |
| } |
| catch (Exception e) |
| { |
| |
| // No-op: should ensure we have clean output |
| } |
| |
| reportPrinter.println("</" + ELEM_HASHTABLE + ">"); |
| } |
| } |
| |
| //----------------------------------------------------- |
| //-------- Test results reporting check* routines -------- |
| //----------------------------------------------------- |
| |
| /** NEEDSDOC Field CHECKPASS_HDR */ |
| private static final String CHECKPASS_HDR = "<" + ELEM_CHECKRESULT + " " |
| + ATTR_RESULT + "=\"" |
| + Reporter.PASS + "\" " |
| + ATTR_DESC + "=\""; |
| |
| /** |
| * Writes out a Pass record with comment. |
| * Record format: <pre><checkresult result="PASS" desc="comment"/></pre> |
| * @param comment comment to log with the pass record. |
| */ |
| public void checkPass(String comment) |
| { |
| |
| if (isReady()) |
| { |
| reportPrinter.println(CHECKPASS_HDR + escapeString(comment) |
| + "\"/>"); |
| } |
| } |
| |
| /** NEEDSDOC Field CHECKAMBG_HDR */ |
| private static final String CHECKAMBG_HDR = "<" + ELEM_CHECKRESULT + " " |
| + ATTR_RESULT + "=\"" |
| + Reporter.AMBG + "\" " |
| + ATTR_DESC + "=\""; |
| |
| /** |
| * Writes out an ambiguous record with comment. |
| * Record format: <pre><checkresult result="AMBG" desc="comment"/></pre> |
| * @param comment comment to log with the ambg record. |
| */ |
| public void checkAmbiguous(String comment) |
| { |
| |
| if (isReady()) |
| { |
| reportPrinter.println(CHECKAMBG_HDR + escapeString(comment) |
| + "\"/>"); |
| } |
| } |
| |
| /** NEEDSDOC Field CHECKFAIL_HDR */ |
| private static final String CHECKFAIL_HDR = "<" + ELEM_CHECKRESULT + " " |
| + ATTR_RESULT + "=\"" |
| + Reporter.FAIL + "\" " |
| + ATTR_DESC + "=\""; |
| |
| /** |
| * Writes out a Fail record with comment. |
| * Record format: <pre><checkresult result="FAIL" desc="comment"/></pre> |
| * @param comment comment to log with the fail record. |
| */ |
| public void checkFail(String comment) |
| { |
| |
| if (isReady()) |
| { |
| reportPrinter.println(CHECKFAIL_HDR + escapeString(comment) |
| + "\"/>"); |
| } |
| } |
| |
| /** NEEDSDOC Field CHECKERRR_HDR */ |
| private static final String CHECKERRR_HDR = "<" + ELEM_CHECKRESULT + " " |
| + ATTR_RESULT + "=\"" |
| + Reporter.ERRR + "\" " |
| + ATTR_DESC + "=\""; |
| |
| /** |
| * Writes out a Error record with comment. |
| * Record format: <pre><checkresult result="ERRR" desc="comment"/></pre> |
| * @param comment comment to log with the error record. |
| */ |
| public void checkErr(String comment) |
| { |
| |
| if (isReady()) |
| { |
| reportPrinter.println(CHECKERRR_HDR + escapeString(comment) |
| + "\"/>"); |
| } |
| } |
| |
| /* EXPERIMENTAL: have duplicate set of check*() methods |
| that all output some form of ID as well as comment. |
| Leave the non-ID taking forms for both simplicity to the |
| end user who doesn't care about IDs as well as for |
| backwards compatibility. |
| */ |
| |
| /** ID_ATTR optimization for extra ID attribute. */ |
| private static final String ATTR_ID = "\" id=\""; |
| /** |
| * Writes out a Pass record with comment. |
| * Record format: <pre><checkresult result="PASS" desc="comment"/></pre> |
| * @param comment comment to log with the pass record. |
| * @param ID token to log with the pass record. |
| */ |
| public void checkPass(String comment, String id) |
| { |
| |
| if (isReady()) |
| { |
| StringBuffer tmp = new StringBuffer(CHECKPASS_HDR + escapeString(comment)); |
| if (id != null) |
| tmp.append(ATTR_ID + escapeString(id)); |
| |
| tmp.append("\"/>"); |
| reportPrinter.println(tmp); |
| } |
| } |
| |
| /** |
| * Writes out an ambiguous record with comment. |
| * Record format: <pre><checkresult result="AMBG" desc="comment"/></pre> |
| * @param comment comment to log with the ambg record. |
| * @param ID token to log with the pass record. |
| */ |
| public void checkAmbiguous(String comment, String id) |
| { |
| |
| if (isReady()) |
| { |
| StringBuffer tmp = new StringBuffer(CHECKAMBG_HDR + escapeString(comment)); |
| if (id != null) |
| tmp.append(ATTR_ID + escapeString(id)); |
| |
| tmp.append("\"/>"); |
| reportPrinter.println(tmp); |
| } |
| } |
| |
| /** |
| * Writes out a Fail record with comment. |
| * Record format: <pre><checkresult result="FAIL" desc="comment"/></pre> |
| * @param comment comment to log with the fail record. |
| * @param ID token to log with the pass record. |
| */ |
| public void checkFail(String comment, String id) |
| { |
| |
| if (isReady()) |
| { |
| StringBuffer tmp = new StringBuffer(CHECKFAIL_HDR + escapeString(comment)); |
| if (id != null) |
| tmp.append(ATTR_ID + escapeString(id)); |
| |
| tmp.append("\"/>"); |
| reportPrinter.println(tmp); |
| } |
| } |
| |
| /** |
| * Writes out a Error record with comment. |
| * Record format: <pre><checkresult result="ERRR" desc="comment"/></pre> |
| * @param comment comment to log with the error record. |
| * @param ID token to log with the pass record. |
| */ |
| public void checkErr(String comment, String id) |
| { |
| |
| if (isReady()) |
| { |
| StringBuffer tmp = new StringBuffer(CHECKERRR_HDR + escapeString(comment)); |
| if (id != null) |
| tmp.append(ATTR_ID + escapeString(id)); |
| |
| tmp.append("\"/>"); |
| reportPrinter.println(tmp); |
| } |
| } |
| |
| //----------------------------------------------------- |
| //-------- Worker routines for XML string escaping -------- |
| //----------------------------------------------------- |
| |
| /** |
| * Lifted from org.apache.xml.serialize.transition.XMLSerializer |
| * |
| * @param ch character to get entity ref for |
| * @return String of entity name |
| */ |
| public static String getEntityRef(char ch) |
| { |
| |
| // Encode special XML characters into the equivalent character references. |
| // These five are defined by default for all XML documents. |
| switch (ch) |
| { |
| case '<' : |
| return "lt"; |
| case '>' : |
| return "gt"; |
| case '"' : |
| return "quot"; |
| case '\'' : |
| return "apos"; |
| case '&' : |
| return "amp"; |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Identifies the last printable character in the Unicode range |
| * that is supported by the encoding used with this serializer. |
| * For 8-bit encodings this will be either 0x7E or 0xFF. |
| * For 16-bit encodings this will be 0xFFFF. Characters that are |
| * not printable will be escaped using character references. |
| * Lifted from org.apache.xml.serialize.transition.BaseMarkupSerializer |
| */ |
| public static int _lastPrintable = 0x7E; |
| |
| /** |
| * Lifted from org.apache.xml.serialize.transition.BaseMarkupSerializer |
| * |
| * @param ch character to escape |
| * @return String that is escaped |
| */ |
| public static String printEscaped(char ch) |
| { |
| |
| String charRef; |
| |
| // If there is a suitable entity reference for this |
| // character, print it. The list of available entity |
| // references is almost but not identical between |
| // XML and HTML. |
| charRef = getEntityRef(ch); |
| |
| if (charRef != null) |
| { |
| |
| //_printer.printText( '&' ); // SC note we need to return a String for |
| //_printer.printText( charRef ); // someone else to serialize |
| //_printer.printText( ';' ); |
| return "&" + charRef + ";"; |
| } |
| else if ((ch >= ' ' && ch <= _lastPrintable && ch != 0xF7) |
| || ch == '\n' || ch == '\r' || ch == '\t') |
| { |
| |
| // If the character is not printable, print as character reference. |
| // Non printables are below ASCII space but not tab or line |
| // terminator, ASCII delete, or above a certain Unicode threshold. |
| //_printer.printText( ch ); |
| return String.valueOf(ch); |
| } |
| else |
| { |
| |
| //_printer.printText( "&#" ); |
| //_printer.printText( Integer.toString( ch ) ); |
| //_printer.printText( ';' ); |
| return "&#" + Integer.toString(ch) + ";"; |
| } |
| } |
| |
| /** |
| * Escapes a string so it may be printed as text content or attribute |
| * value. Non printable characters are escaped using character references. |
| * Where the format specifies a deault entity reference, that reference |
| * is used (e.g. <tt>&lt;</tt>). |
| * Lifted from org.apache.xml.serialize.transition.BaseMarkupSerializer |
| * |
| * @param source The string to escape |
| * @return String after escaping - needed for our application |
| */ |
| public static String escapeString(String source) |
| { |
| // Check for null; just return null (callers shouldn't care) |
| if (source == null) |
| { |
| return null; |
| } |
| StringBuffer sb = new StringBuffer(); |
| final int n = source.length(); |
| |
| for (int i = 0; i < n; ++i) |
| { |
| |
| //char c = source.charAt( i ); |
| sb.append(printEscaped(source.charAt(i))); |
| } |
| |
| return sb.toString(); |
| } |
| } // end of class XMLFileLogger |
| |