added a mode for posting messages via XML-RPC that works better with HTML

E-mail messages
This commit is contained in:
Eric J. Bowersox 2002-03-11 06:47:01 +00:00
parent bb2b63c837
commit a459e02e79
11 changed files with 481 additions and 98 deletions

View File

@ -39,12 +39,32 @@ topic = xreq.getParamTopic(3,conf);
if ("postMessage"==call_name) if ("postMessage"==call_name)
{ // venice:conferencing.topic.postMessage <session-id> <community> <conference> <topic> <pseud> <text> { // venice:conferencing.topic.postMessage <session-id> <community> <conference> <topic> <pseud> <text>
// [<mode>]
// Posts a message, returns the message number within the topic // Posts a message, returns the message number within the topic
if (xreq.paramCount!=6) if ((xreq.paramCount!=6) && (xreq.paramCount!=7))
vlib.output(new XmlRpcFault(XmlRpcFault.INVALID_PARAMS,"parameter count mismatch")); vlib.output(new XmlRpcFault(XmlRpcFault.INVALID_PARAMS,"parameter count mismatch"));
else else
{ // post the message { // determine the mode
msg = topic.postNewMessage(0,xreq.getParamString(4),xreq.getParamString(5)); mode = TopicContext.POST_MODE_NORMAL;
if (xreq.paramCount==7)
{ // interpret the posting mode
s = xreq.getParamString(6);
if (s.equalsIgnoreCase("normal"))
mode = TopicContext.POST_MODE_NORMAL;
else if (s.equalsIgnoreCase("email"))
mode = TopicContext.POST_MODE_EMAIL;
else
{ // invalid mode parameter
vlib.output(new XmlRpcFault(XmlRpcFault.INVALID_PARAMS,"invalid post mode"));
vlib.done();
} // end else
} // end if
// else use the default of "normal"
// post the message
msg = topic.postNewMessage(0,xreq.getParamString(4),xreq.getParamString(5),mode);
vlib.output(vlib.createInteger(msg.postNumber)); vlib.output(vlib.createInteger(msg.postNumber));
} // end else } // end else

View File

@ -11,7 +11,7 @@
* *
* The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>, * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
* for Silverwrist Design Studios. Portions created by Eric J. Bowersox are * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are
* Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. * Copyright (C) 2001-02 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved.
* *
* Contributor(s): * Contributor(s):
*/ */
@ -26,6 +26,10 @@ import com.silverwrist.venice.htmlcheck.HTMLChecker;
public interface TopicContext public interface TopicContext
{ {
// Modes for postNewMessage()
public static final int POST_MODE_NORMAL = 0;
public static final int POST_MODE_EMAIL = 1;
public abstract void refresh() throws DataException; public abstract void refresh() throws DataException;
public abstract int getTopicID(); public abstract int getTopicID();
@ -72,6 +76,9 @@ public interface TopicContext
public abstract TopicMessageContext getMessage(int number) throws DataException, AccessError; public abstract TopicMessageContext getMessage(int number) throws DataException, AccessError;
public abstract TopicMessageContext postNewMessage(long parent, String pseud, String text, int mode)
throws DataException, AccessError;
public abstract TopicMessageContext postNewMessage(long parent, String pseud, String text) public abstract TopicMessageContext postNewMessage(long parent, String pseud, String text)
throws DataException, AccessError; throws DataException, AccessError;

View File

@ -254,6 +254,13 @@ class PublishedMessageTopicImpl implements TopicContext
} // end getMessage } // end getMessage
public TopicMessageContext postNewMessage(long parent, String pseud, String text, int mode)
throws AccessError
{
throw new AccessError("cannot perform this function from a read-only topic view");
} // end postNewMessage
public TopicMessageContext postNewMessage(long parent, String pseud, String text) throws AccessError public TopicMessageContext postNewMessage(long parent, String pseud, String text) throws AccessError
{ {
throw new AccessError("cannot perform this function from a read-only topic view"); throw new AccessError("cannot perform this function from a read-only topic view");

View File

@ -631,11 +631,14 @@ class TopicUserContextImpl implements TopicContext
} // end getMessage } // end getMessage
public TopicMessageContext postNewMessage(long parent, String pseud, String text) public TopicMessageContext postNewMessage(long parent, String pseud, String text, int mode)
throws DataException, AccessError throws DataException, AccessError
{ {
if (logger.isInfoEnabled()) if (logger.isInfoEnabled())
logger.info("postNewMessage(" + parent + ", '" + pseud + "',<text>) entry"); logger.info("postNewMessage(" + parent + ", '" + pseud + "',<text>," + mode + ") entry");
if ((mode!=POST_MODE_NORMAL) && (mode!=POST_MODE_EMAIL))
throw new IllegalArgumentException("invalid mode parameter");
if (!(env.getConference().userCanPost())) if (!(env.getConference().userCanPost()))
{ // they can't post in this topic! { // they can't post in this topic!
@ -665,9 +668,25 @@ class TopicUserContextImpl implements TopicContext
} // end if } // end if
// figure out which HTML checkers to use
int pseud_ch_index = EngineBackend.HTMLC_POST_PSEUD;
int body_ch_index = EngineBackend.HTMLC_POST_BODY;
if (mode==POST_MODE_NORMAL)
{ // configure for normal posting
pseud_ch_index = EngineBackend.HTMLC_POST_PSEUD;
body_ch_index = EngineBackend.HTMLC_POST_BODY;
} // end if
else if (mode==POST_MODE_EMAIL)
{ // configure for E-mail posting
pseud_ch_index = EngineBackend.HTMLC_POST_PSEUD;
body_ch_index = EngineBackend.HTMLC_POST_BODY_EMAIL;
} // end else if
// preprocess the two arguments through HTML checkers // preprocess the two arguments through HTML checkers
HTMLChecker pseud_ch = env.getEngine().createCheckerObject(EngineBackend.HTMLC_POST_PSEUD); HTMLChecker pseud_ch = env.getEngine().createCheckerObject(pseud_ch_index);
HTMLChecker text_ch = env.getEngine().createCheckerObject(EngineBackend.HTMLC_POST_BODY); HTMLChecker text_ch = env.getEngine().createCheckerObject(body_ch_index);
text_ch.setContextValue("PostLinkDecoderContext",env.getConference().createDecoderContext(topicnum)); text_ch.setContextValue("PostLinkDecoderContext",env.getConference().createDecoderContext(topicnum));
try try
{ // run both arguments through the HTML checker { // run both arguments through the HTML checker
@ -887,7 +906,14 @@ class TopicUserContextImpl implements TopicContext
return new TopicMessageUserContextImpl(env,new_post_id,parent,topicid,new_post_num,text_linecount, return new TopicMessageUserContextImpl(env,new_post_id,parent,topicid,new_post_num,text_linecount,
env.getUserID(),posted_date,real_pseud); env.getUserID(),posted_date,real_pseud);
} // end postMessage } // end postNewMessage
public TopicMessageContext postNewMessage(long parent, String pseud, String text)
throws DataException, AccessError
{
return this.postNewMessage(parent,pseud,text,POST_MODE_NORMAL);
} // end postNewMessage
public HTMLChecker getPreviewChecker() public HTMLChecker getPreviewChecker()
{ {

View File

@ -739,7 +739,7 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
LazyTreeLexicon lex = new LazyTreeLexicon((String[])(dictionary_tmp.toArray(new String[0]))); LazyTreeLexicon lex = new LazyTreeLexicon((String[])(dictionary_tmp.toArray(new String[0])));
spell_rewriter.addDictionary(lex); spell_rewriter.addDictionary(lex);
html_configs = new HTMLCheckerConfig[5]; // create the array html_configs = new HTMLCheckerConfig[6]; // create the array
// Create the HTML checker config used to post body text to the database. // Create the HTML checker config used to post body text to the database.
HTMLCheckerConfig cfg = HTMLCheckerCreator.create(); HTMLCheckerConfig cfg = HTMLCheckerCreator.create();
@ -805,9 +805,34 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
cfg.setProcessAngles(true); cfg.setProcessAngles(true);
cfg.setProcessParens(false); cfg.setProcessParens(false);
cfg.setDiscardHTMLTags(true); cfg.setDiscardHTMLTags(true);
cfg.setDiscardRejectedHTML(true);
cfg.configureNormalTagSet(); cfg.configureNormalTagSet();
html_configs[HTMLC_MAIL_POST] = cfg; html_configs[HTMLC_MAIL_POST] = cfg;
// Create the HTML checker config used to post body text to the database from an E-mail message.
cfg = HTMLCheckerCreator.create();
cfg.setWordWrapLength((short)55);
cfg.setRewrapLines(true);
cfg.setProcessAngles(true);
cfg.setProcessParens(true);
cfg.setDiscardHTMLTags(false);
cfg.setDiscardRejectedHTML(true);
cfg.setDiscardHTMLComments(true);
cfg.setDiscardXMLConstructs(true);
cfg.addOutputFilter(html_filter);
cfg.addOutputFilter(sql_filter);
cfg.addRawOutputFilter(sql_filter);
cfg.addStringRewriter(email_rewriter);
cfg.addStringRewriter(url_rewriter);
cfg.addTagRewriter(postlink_rewriter);
cfg.addTagRewriter(username_rewriter);
cfg.addTagRewriter(email_rewriter);
cfg.addTagRewriter(url_rewriter);
cfg.addParenRewriter(username_rewriter);
cfg.configureNormalTagSet();
cfg.disallowTagSet(HTMLTagSets.FONT_FORMAT);
html_configs[HTMLC_POST_BODY_EMAIL] = cfg;
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
logger.debug("initialize() complete :-)"); logger.debug("initialize() complete :-)");

View File

@ -37,6 +37,7 @@ public interface EngineBackend
public static final int HTMLC_PREVIEW_BODY = 2; public static final int HTMLC_PREVIEW_BODY = 2;
public static final int HTMLC_ESCAPE_BODY_PSEUD = 3; public static final int HTMLC_ESCAPE_BODY_PSEUD = 3;
public static final int HTMLC_MAIL_POST = 4; public static final int HTMLC_MAIL_POST = 4;
public static final int HTMLC_POST_BODY_EMAIL = 5;
// Integer parameter indexes // Integer parameter indexes
public static final int IP_POSTSPERPAGE = 0; public static final int IP_POSTSPERPAGE = 0;

View File

@ -7,11 +7,11 @@
* WARRANTY OF ANY KIND, either express or implied. See the License for the specific * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
* language governing rights and limitations under the License. * language governing rights and limitations under the License.
* *
* The Original Code is the Venice Web Community System. * The Original Code is the Venice Web Communities System.
* *
* The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>, * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
* for Silverwrist Design Studios. Portions created by Eric J. Bowersox are * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are
* Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. * Copyright (C) 2001-02 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved.
* *
* Contributor(s): * Contributor(s):
*/ */
@ -23,6 +23,10 @@ public interface HTMLCheckerConfig extends HTMLTagSets
public abstract void setWordWrapLength(short val); public abstract void setWordWrapLength(short val);
public abstract boolean getRewrapLines();
public abstract void setRewrapLines(boolean val);
public abstract boolean getProcessAngles(); public abstract boolean getProcessAngles();
public abstract void setProcessAngles(boolean val); public abstract void setProcessAngles(boolean val);
@ -35,6 +39,18 @@ public interface HTMLCheckerConfig extends HTMLTagSets
public abstract void setDiscardHTMLTags(boolean val); public abstract void setDiscardHTMLTags(boolean val);
public abstract boolean getDiscardRejectedHTML();
public abstract void setDiscardRejectedHTML(boolean val);
public abstract boolean getDiscardHTMLComments();
public abstract void setDiscardHTMLComments(boolean val);
public abstract boolean getDiscardXMLConstructs();
public abstract void setDiscardXMLConstructs(boolean val);
public abstract String getAnchorTail(); public abstract String getAnchorTail();
public abstract void setAnchorTail(String s); public abstract void setAnchorTail(String s);

View File

@ -7,11 +7,11 @@
* WARRANTY OF ANY KIND, either express or implied. See the License for the specific * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
* language governing rights and limitations under the License. * language governing rights and limitations under the License.
* *
* The Original Code is the Venice Web Community System. * The Original Code is the Venice Web Communities System.
* *
* The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>, * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
* for Silverwrist Design Studios. Portions created by Eric J. Bowersox are * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are
* Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. * Copyright (C) 2001-02 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved.
* *
* Contributor(s): * Contributor(s):
*/ */
@ -134,5 +134,9 @@ public interface HTMLTagSets
* including server-side components. These are generally not allowed. * including server-side components. These are generally not allowed.
*/ */
public static final int JAVA_SERVER = 24; public static final int JAVA_SERVER = 24;
/**
* Denotes HTML comments. These are generally not allowed.
*/
public static final int COMMENT = 25;
} // end interface HTMLTagSets } // end interface HTMLTagSets

View File

@ -11,7 +11,7 @@
* *
* The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>, * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
* for Silverwrist Design Studios. Portions created by Eric J. Bowersox are * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are
* Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. * Copyright (C) 2001-02 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved.
* *
* Contributor(s): * Contributor(s):
*/ */
@ -34,9 +34,13 @@ public class HTMLCheckerConfigImpl implements HTMLCheckerConfig
*-------------------------------------------------------------------------------- *--------------------------------------------------------------------------------
*/ */
private boolean rewrap_lines = false; // re-word-wrap lines?
private boolean process_angles = true; // process angle-bracketed strings? private boolean process_angles = true; // process angle-bracketed strings?
private boolean process_parens = true; // process parenthesized strings? private boolean process_parens = true; // process parenthesized strings?
private boolean discard_html_tags = false; // discard all HTML tags? private boolean discard_html_tags = false; // discard all HTML tags?
private boolean discard_rejected_html = false; // discard HTML tags that are rejected?
private boolean discard_html_comments = false; // discard HTML comments?
private boolean discard_xml_constructs = false; // discard XML constructs (namespaced tags)?
private short word_wrap_length = 0; // word wrap length private short word_wrap_length = 0; // word wrap length
private String anchor_tail = DEFAULT_ANCHOR_TAIL; // the tail end of the anchor private String anchor_tail = DEFAULT_ANCHOR_TAIL; // the tail end of the anchor
private BitSet allowed_tagsets = new BitSet(); // which tagsets are allowed? private BitSet allowed_tagsets = new BitSet(); // which tagsets are allowed?
@ -78,6 +82,18 @@ public class HTMLCheckerConfigImpl implements HTMLCheckerConfig
} // end setWordWrapLength } // end setWordWrapLength
public boolean getRewrapLines()
{
return rewrap_lines;
} // end getRewrapLines
public void setRewrapLines(boolean val)
{
rewrap_lines = val;
} // end setRewrapLines
public boolean getProcessAngles() public boolean getProcessAngles()
{ {
return process_angles; return process_angles;
@ -114,6 +130,42 @@ public class HTMLCheckerConfigImpl implements HTMLCheckerConfig
} // end setDiscardHTMLTags } // end setDiscardHTMLTags
public boolean getDiscardRejectedHTML()
{
return discard_rejected_html;
} // end getDiscardRejectedHTML
public void setDiscardRejectedHTML(boolean val)
{
discard_rejected_html = val;
} // end setDiscardRejectedHTML
public boolean getDiscardHTMLComments()
{
return discard_html_comments;
} // end getDiscardHTMLComments
public void setDiscardHTMLComments(boolean val)
{
discard_html_comments = val;
} // end setDiscardHTMLComments
public boolean getDiscardXMLConstructs()
{
return discard_xml_constructs;
} // end getDiscardXMLConstructs
public void setDiscardXMLConstructs(boolean val)
{
discard_xml_constructs = val;
} // end setDiscardXMLConstructs
public String getAnchorTail() public String getAnchorTail()
{ {
return anchor_tail; return anchor_tail;
@ -158,6 +210,8 @@ public class HTMLCheckerConfigImpl implements HTMLCheckerConfig
public void addParenRewriter(Rewriter rewriter) public void addParenRewriter(Rewriter rewriter)
{ {
paren_rewriters.add(rewriter);
} // end addParenRewriter } // end addParenRewriter
public boolean isTagSetAllowed(int setid) public boolean isTagSetAllowed(int setid)

View File

@ -11,7 +11,7 @@
* *
* The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>, * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
* for Silverwrist Design Studios. Portions created by Eric J. Bowersox are * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are
* Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. * Copyright (C) 2001-02 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved.
* *
* Contributor(s): * Contributor(s):
*/ */
@ -79,6 +79,7 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
private static final short ST_TAG = 3; private static final short ST_TAG = 3;
private static final short ST_PAREN = 4; private static final short ST_PAREN = 4;
private static final short ST_TAGQUOTE = 5; private static final short ST_TAGQUOTE = 5;
private static final short ST_NEWLINE = 6;
/*-------------------------------------------------------------------------------- /*--------------------------------------------------------------------------------
* Internal constants * Internal constants
@ -139,12 +140,26 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
*-------------------------------------------------------------------------------- *--------------------------------------------------------------------------------
*/ */
/**
* Returns <CODE>true</CODE> if this character belongs as part of a word, <CODE>false</CODE> if not.
*
* @param ch Character to be tested.
* @return See above.
*/
private static final boolean isWordChar(char ch) private static final boolean isWordChar(char ch)
{ {
return (Character.isUpperCase(ch) || Character.isLowerCase(ch) || (ch=='-') || (ch=='\'')); return (Character.isUpperCase(ch) || Character.isLowerCase(ch) || (ch=='-') || (ch=='\''));
} // end isWordChar } // end isWordChar
/**
* Returns the maximum length of a &quot;run&quot; of word characters or non-word characters in the
* buffer, beginning at the specified start point, before a character of the opposite classification.
*
* @param buf The buffer to look through.
* @param start The start position to look at.
* @return See above.
*/
private static final int getRunLength(StringBuffer buf, int start) private static final int getRunLength(StringBuffer buf, int start)
{ {
boolean word_char = isWordChar(buf.charAt(start)); boolean word_char = isWordChar(buf.charAt(start));
@ -161,13 +176,27 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
} // end getRunLength } // end getRunLength
/**
* Returns the maximum length of a &quot;run&quot; of word characters or non-word characters in the
* buffer, counting from the start of the buffer, before a character of the opposite classification.
*
* @param buf The buffer to look through.
* @return See above.
*/
private static final int getRunLength(StringBuffer buf) private static final int getRunLength(StringBuffer buf)
{ {
return getRunLength(buf,0); return getRunLength(buf,0);
} // end getRunLength } // end getRunLength
private void copyRewriters(ArrayList dest, List source) /**
* Copies the <CODE>Rewriter</CODE> objects from an outside list to an internal list, wrapping
* named rewriters in <CODE>CountingRewriter</CODE> objects as appropriate.
*
* @param dest Destination to copy rewriters to.
* @param source List to copy rewriters from.
*/
private final void copyRewriters(ArrayList dest, List source)
{ {
Iterator it = source.iterator(); Iterator it = source.iterator();
while (it.hasNext()) while (it.hasNext())
@ -190,7 +219,17 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
} // end copyRewriters } // end copyRewriters
private MarkupData attemptRewrite(List rewriters, String data) /**
* Use the specified list of rewriters to attempt to rewrite the specified string data.
* The first rewriter in the list that returns a valid <CODE>MarkupData</CODE> object takes
* precedence.
*
* @param rewriters List of rewriters to try against the rewriter data.
* @param data String data to attempt to rewrite.
* @return A <CODE>MarkupData</CODE> object that contains the marked-up data to output, or
* <CODE>null</CODE> if no rewriter handled the data.
*/
private final MarkupData attemptRewrite(List rewriters, String data)
{ {
Iterator it = rewriters.iterator(); Iterator it = rewriters.iterator();
MarkupData rc = null; MarkupData rc = null;
@ -205,7 +244,10 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
} // end attemptRewrite } // end attemptRewrite
private void initState() /**
* Initializes the internal state of the parser.
*/
private final void initState()
{ {
output_buffer = new StringBuffer(1024); output_buffer = new StringBuffer(1024);
temp_buffer = new StringBuffer(64); temp_buffer = new StringBuffer(64);
@ -213,7 +255,10 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
} // end initState } // end initState
private void killState() /**
* Erases any temporary data that is no longer needed after the parser finishes.
*/
private final void killState()
{ {
temp_buffer = null; temp_buffer = null;
if (tag_stack!=null) if (tag_stack!=null)
@ -222,7 +267,15 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
} // end killState } // end killState
private void emitChar(char ch, List filters, boolean count_cols) /**
* Emits one character to the output of the HTML checker, running it through a list of defined filters.
*
* @param ch Character to output.
* @param filters List of filters to use to attempt to process the character.
* @param count_cols <CODE>true</CODE> if the character output adds to the column counter,
* <CODE>false</CODE> if not.
*/
private final void emitChar(char ch, List filters, boolean count_cols)
{ {
boolean handled = false; boolean handled = false;
if (filters.size()>0) if (filters.size()>0)
@ -244,7 +297,15 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
} // end emitChar } // end emitChar
private void emitString(String str, List filters, boolean count_cols) /**
* Emits a character string to the output of the HTML checker, running it through a list of defined filters.
*
* @param str String to output.
* @param filters List of filters to use to attempt to process the string.
* @param count_cols <CODE>true</CODE> if the characters output add to the column counter,
* <CODE>false</CODE> if not.
*/
private final void emitString(String str, List filters, boolean count_cols)
{ {
boolean real_count_cols = count_cols && (config.getWordWrapLength()>0); boolean real_count_cols = count_cols && (config.getWordWrapLength()>0);
@ -309,7 +370,11 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
} // end emitString } // end emitString
private void emitLineBreak() /**
* Emits a line break to the output of the HTML checker, resetting the column counter and advancing
* the line counter.
*/
private final void emitLineBreak()
{ {
emitString("\r\n",config.getRawOutputFilters(),false); emitString("\r\n",config.getRawOutputFilters(),false);
if (config.getWordWrapLength()>0) if (config.getWordWrapLength()>0)
@ -318,14 +383,24 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
} // end emitLineBreak } // end emitLineBreak
private void emitPossibleLineBreak() /**
* If the current line of text contains more characters than will fit on the line, emit a line break.
* Disabled if the checker does not word-wrap.
*/
private final void emitPossibleLineBreak()
{ {
if ((config.getWordWrapLength()>0) && (nobreak_count<=0) && (columns>=config.getWordWrapLength())) if ((config.getWordWrapLength()>0) && (nobreak_count<=0) && (columns>=config.getWordWrapLength()))
emitLineBreak(); emitLineBreak();
} // end emitPossibleLineBreak } // end emitPossibleLineBreak
private void ensureSpaceOnLine(int nchars) /**
* Ensure that the current line has a certain number of characters of space left on it; if it does not,
* emit a line break.
*
* @param nchars Number of characters to reserve on the line.
*/
private final void ensureSpaceOnLine(int nchars)
{ {
if ((config.getWordWrapLength()>0) && (nobreak_count<=0)) if ((config.getWordWrapLength()>0) && (nobreak_count<=0))
{ // line break might be required here { // line break might be required here
@ -337,71 +412,111 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
} // end ensureSpaceOnLine } // end ensureSpaceOnLine
private void emitMarkupData(MarkupData md) private final void emitMarkupData(MarkupData md)
{ {
ensureSpaceOnLine(md.getText().length()); ensureSpaceOnLine(md.getText().length());
emitString(md.getBeginMarkup(),config.getRawOutputFilters(),false); List raw_filters = config.getRawOutputFilters();
emitString(md.getBeginMarkup(),raw_filters,false);
emitString(md.getText(),config.getOutputFilters(),true); emitString(md.getText(),config.getOutputFilters(),true);
emitString(md.getEndMarkup(),config.getRawOutputFilters(),false); emitString(md.getEndMarkup(),raw_filters,false);
} // end emitMarkupData } // end emitMarkupData
private void emitMarkupData(MarkupData md, char prefix, char suffix) private final void emitMarkupData(MarkupData md, char prefix, char suffix)
{ {
ensureSpaceOnLine(md.getText().length() + 2); ensureSpaceOnLine(md.getText().length() + 2);
emitChar(prefix,config.getOutputFilters(),true); List raw_filters = config.getRawOutputFilters();
emitString(md.getBeginMarkup(),config.getRawOutputFilters(),false); List cooked_filters = config.getOutputFilters();
emitString(md.getText(),config.getOutputFilters(),true); emitChar(prefix,cooked_filters,true);
emitString(md.getEndMarkup(),config.getRawOutputFilters(),false); emitString(md.getBeginMarkup(),raw_filters,false);
emitChar(suffix,config.getOutputFilters(),true); emitString(md.getText(),cooked_filters,true);
emitString(md.getEndMarkup(),raw_filters,false);
emitChar(suffix,cooked_filters,true);
} // end emitMarkupData } // end emitMarkupData
private void doFlushWhitespace() private final void doFlushWhitespace()
{ {
while (temp_buffer.length()>0) int output_len = temp_buffer.length();
{ // calculate where the next line break is if (output_len<=0)
int line_break = temp_buffer.toString().indexOf('\n'); return;
int output_len = line_break; boolean force_line_break = false;
boolean force_line_break = false;
if (output_len<0)
output_len = temp_buffer.length();
if ((config.getWordWrapLength()>0) && (nobreak_count<=0)) if ((config.getWordWrapLength()>0) && (nobreak_count<=0))
{ // adjust output if necessary for word wrapping { // adjust output if necessary for word wrapping
int remain_space = (int)(config.getWordWrapLength() - columns); int remain_space = (int)(config.getWordWrapLength() - columns);
if (remain_space<output_len) if (remain_space<output_len)
output_len = remain_space; output_len = remain_space;
if (output_len<=0) // this means that NONE of the whitespace would fit on this line... if (output_len<=0)
force_line_break = true; // we need a line break to fill in! { // this means that NONE of the whitespace would fit on this line...
force_line_break = true; // we need a line break to fill in!
output_len = 0;
} // end if } // end if
if (output_len>0) } // end if
emitString(temp_buffer.substring(0,output_len),config.getOutputFilters(),true);
if (line_break>=0) if (force_line_break)
{ // there's a line break present - emit the line break emitLineBreak();
emitLineBreak(); // output the line break character if (output_len>0)
if (++line_break<temp_buffer.length()) emitString(temp_buffer.substring(0,output_len),config.getOutputFilters(),true);
temp_buffer.delete(0,line_break);
else
temp_buffer.setLength(0);
} // end if // clear out the buffer
else temp_buffer.setLength(0);
{ // no more line breaks on this line - clear out the buffer
if (force_line_break)
emitLineBreak(); // notice we can only force a line break if we didn't have one in the text
temp_buffer.setLength(0);
} // end else
} // end while (still data in temp buffer)
} // end doFlushWhitespace } // end doFlushWhitespace
private void emitFromStartOfTempBuffer(int nchars) private final void doFlushNewlines()
{
//logger.debug("FlushNewLines");
int line_breaks = 0, crs = 0;
for (int i=0; i<temp_buffer.length(); i++)
{ // convert the \r and \n string into a line break count
char ch = temp_buffer.charAt(i);
if (ch=='\r')
{ // count carriage returns
//logger.debug("CR");
crs++;
} // end if
else if (ch=='\n')
{ // count line breaks...
//logger.debug("LF");
crs = 0;
line_breaks++;
} // end else if
} // end for
if (crs>0)
line_breaks++;
//logger.debug("Total line breaks: " + line_breaks);
if (config.getRewrapLines())
{ // rewrap lines forces a lot of adjustment in lines...
if (line_breaks<2)
{ // convert a single line break to whitespace
temp_buffer.setLength(0);
temp_buffer.append(' ');
state = ST_WHITESPACE;
return;
} // end if
else
line_breaks = 2; // compress out multiple blank lines
} // end if
while ((line_breaks--)>0)
emitLineBreak(); // emit the line breaks
temp_buffer.setLength(0); // clear out the buffer
state = ST_WHITESPACE;
} // end doFlushNewlines
private final void emitFromStartOfTempBuffer(int nchars)
{ {
if (nchars<=0) if (nchars<=0)
return; // can't emit less than 1 character! return; // can't emit less than 1 character!
@ -439,7 +554,7 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
} // end emitFromStartOfTempBuffer } // end emitFromStartOfTempBuffer
private void doFlushString() private final void doFlushString()
{ {
MarkupData md = attemptRewrite(string_rewriters,temp_buffer.toString()); MarkupData md = attemptRewrite(string_rewriters,temp_buffer.toString());
if (md!=null) if (md!=null)
@ -529,7 +644,7 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
} // end doFlushString } // end doFlushString
private boolean handleAsHTML() private final boolean handleAsHTML()
{ {
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
logger.debug("handleAsHTML(): candidate buffer = [" + temp_buffer.toString() + "]"); logger.debug("handleAsHTML(): candidate buffer = [" + temp_buffer.toString() + "]");
@ -583,13 +698,24 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
// Get the HTML tag set index for this tag, and see if we allow that set. // Get the HTML tag set index for this tag, and see if we allow that set.
int tag_set_id = TagRepository.tagIndexToSet(tag_index); int tag_set_id = TagRepository.tagIndexToSet(tag_index);
if (!(config.isTagSetAllowed(tag_set_id)) && !(config.getDiscardHTMLTags())) if (!(config.isTagSetAllowed(tag_set_id)))
{ // we're not allowing it, we're not discarding it, so punt! { // the tag is not allowed - either discard it or leave it in verbatim
if (logger.isDebugEnabled()) if (config.getDiscardHTMLTags() || config.getDiscardRejectedHTML())
logger.debug("<" + poss_tag_name + "> is not allowed in this context"); { // throw this tag the hell away!
return false; if (logger.isDebugEnabled())
logger.debug("<" + poss_tag_name + "> tag rejected and discarded");
return true;
} // end if } // end if
else
{ // kick the tag out and let some other code deal with it
if (logger.isDebugEnabled())
logger.debug("<" + poss_tag_name + "> is not allowed in this context");
return false;
} // end if
} // end if (tag rejected by HTML rules)
if (!(config.getDiscardHTMLTags()) && tagobj.balanceTags()) if (!(config.getDiscardHTMLTags()) && tagobj.balanceTags())
{ // this tag needs to be balanced - here is where we manipulate the stack { // this tag needs to be balanced - here is where we manipulate the stack
@ -648,9 +774,10 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
logger.debug("real tag data = [" + real_tag_data + "]"); logger.debug("real tag data = [" + real_tag_data + "]");
// Emit the tag to the output. // Emit the tag to the output.
emitChar('<',config.getRawOutputFilters(),false); List filters = config.getRawOutputFilters();
emitString(real_tag_data,config.getRawOutputFilters(),false); emitChar('<',filters,false);
emitChar('>',config.getRawOutputFilters(),false); emitString(real_tag_data,filters,false);
emitChar('>',filters,false);
// Determine whether this tag causes a "logical line break." // Determine whether this tag causes a "logical line break."
boolean logical_line_break = false; boolean logical_line_break = false;
@ -673,10 +800,85 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
return true; // handled! return true; // handled!
} // end handleAsHTML() } // end handleAsHTML
private void finishTag() /**
* Returns <CODE>true</CODE> if the temporary buffer contains the start of an HTML comment. (The
* leading and trailing angle brackets are assumed.)
*
* @return See above.
*/
private final boolean containsHTMLComment()
{ {
return ((temp_buffer.length()>=3) && (temp_buffer.substring(0,3).equals("!--")));
} // end containsHTMLComment
/**
* Returns <CODE>true</CODE> if the temporary buffer contains a complete HTML comment. (The leading
* and trailing angle brackets are assumed.)
*
* @return See above.
*/
private final boolean containsCompleteHTMLComment()
{
int l = temp_buffer.length();
// note that a minimum HTML comment is <!---->, i.e. "<!--" followed by "-->" with no characters
// in between...
return ((l>=5) && (temp_buffer.substring(0,3).equals("!--"))
&& (temp_buffer.substring(l-2,l).equals("--")));
} // end containsCompleteHTMLComment
/**
* Returns <CODE>true</CODE> if the temporary buffer contains an XML construct, i.e. a tag that
* contains a ':', and may or may not have a leading '/'. (The leading and trailing angle brackets
* are assumed.)
*
* @return See above.
*/
private final boolean containsXMLConstruct()
{
int ptr = 0;
if ((temp_buffer.length()>1) && (temp_buffer.charAt(0)=='/'))
ptr++;
while (ptr<temp_buffer.length())
{ // look for a ':' in the "tag name" portion of the HTML tag
char ch = temp_buffer.charAt(ptr++);
if (ch==':')
return true;
if (Character.isWhitespace(ch))
break;
} // end while
return false;
} // end containsXMLConstruct
private final void finishTag()
{
if (containsHTMLComment())
{ // we are dealing with at least the start of an HTML comment - is it a complete one?
if (!(containsCompleteHTMLComment()))
return; // we have an HTML comment but it's not complete yet - bail out
if (!(config.getDiscardHTMLComments()))
{ // output the comment in the raw
List filters = config.getRawOutputFilters();
emitChar('<',filters,false);
emitString(temp_buffer.toString(),filters,false);
emitChar('>',filters,false);
} // end if
// clear our state and return to parsing
temp_buffer.setLength(0);
state = ST_WHITESPACE;
return;
} // end if
if (handleAsHTML()) if (handleAsHTML())
{ // the tag has been handled as an HTML tag - bail out immediately { // the tag has been handled as an HTML tag - bail out immediately
temp_buffer.setLength(0); temp_buffer.setLength(0);
@ -696,6 +898,14 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
} // end if } // end if
if (config.getDiscardXMLConstructs() && containsXMLConstruct())
{ // the tag is an XML construct, and is to be discarded
temp_buffer.setLength(0);
state = ST_WHITESPACE;
return;
} // end if
// This tag has been rejected! We need to process it normally, as character data. // This tag has been rejected! We need to process it normally, as character data.
String rejection = temp_buffer.toString(); String rejection = temp_buffer.toString();
temp_buffer.setLength(0); temp_buffer.setLength(0);
@ -707,7 +917,7 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
} // end finishTag } // end finishTag
private void finishParen() private final void finishParen()
{ {
// Try to handle the paren element using a paren rewriter. // Try to handle the paren element using a paren rewriter.
MarkupData md = attemptRewrite(paren_rewriters,temp_buffer.toString()); MarkupData md = attemptRewrite(paren_rewriters,temp_buffer.toString());
@ -733,7 +943,7 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
} // end finishParen } // end finishParen
private void parse(String str) private final void parse(String str)
{ {
int i = 0; int i = 0;
while (i<str.length()) while (i<str.length())
@ -747,17 +957,17 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
{ // Whitespace handling - look at the character { // Whitespace handling - look at the character
switch (ch) switch (ch)
{ {
case ' ': // append spaces, tabs, and newlines verbatim to the temp buffer case ' ': // append spaces and tabs verbatim to the temp buffer
case '\t': case '\t':
case '\n':
temp_buffer.append(ch); temp_buffer.append(ch);
i++; i++;
break; break;
case '\r': // compress 1 or more \r's followed by optional \n to a single \n case '\r': // flush the whitespace and go to NEWLINE state
if ( (i==(str.length()-1)) case '\n':
|| ((str.charAt(i+1)!='\r') && (str.charAt(i+1)!='\n'))) doFlushWhitespace();
temp_buffer.append('\n'); state = ST_NEWLINE;
temp_buffer.append(ch);
i++; i++;
break; break;
@ -810,16 +1020,18 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
{ {
case ' ': // whitespace - drop back to whitespace mode case ' ': // whitespace - drop back to whitespace mode
case '\t': case '\t':
case '\n':
doFlushString(); doFlushString();
state = ST_WHITESPACE; state = ST_WHITESPACE;
temp_buffer.append(ch); temp_buffer.append(ch);
i++; i++;
break; break;
case '\r': // handle \r processing in ST_WHITESPACE 'cause it's complicated case '\r': // newline characters - flush out the buffer and go to NEWLINE state
case '\n':
doFlushString(); doFlushString();
state = ST_WHITESPACE; state = ST_NEWLINE;
temp_buffer.append(ch);
i++;
break; break;
case '<': // left angle bracket - may be a start-of-tag case '<': // left angle bracket - may be a start-of-tag
@ -973,6 +1185,17 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
i++; i++;
break; break;
case ST_NEWLINE:
if ((ch=='\r') || (ch=='\n'))
{ // handle newlines
temp_buffer.append(ch);
i++;
} // end if
else // flush out the newlines and click back to whitespace mode
doFlushNewlines();
break;
default: default:
throw new IllegalStateException("invalid parser state value"); throw new IllegalStateException("invalid parser state value");
@ -1024,7 +1247,8 @@ class HTMLCheckerImpl implements HTMLChecker, HTMLCheckerBackend, RewriterServic
switch (state) switch (state)
{ {
case ST_WHITESPACE: case ST_WHITESPACE:
break; // discard any whitespace at the end of output case ST_NEWLINE:
break; // discard any whitespace or newlines at the end of output
case ST_CHARS: case ST_CHARS:
doFlushString(); // flush out the temporary buffer doFlushString(); // flush out the temporary buffer

View File

@ -11,7 +11,7 @@
* *
* The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>, * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
* for Silverwrist Design Studios. Portions created by Eric J. Bowersox are * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are
* Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. * Copyright (C) 2001-02 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved.
* *
* Contributor(s): * Contributor(s):
*/ */
@ -124,8 +124,7 @@ class TagRepository implements HTMLTagSets
*/ */
static static
{ { // begin enshrining the tags!
// begin enshrining the tags!
enshrineTag(new SimpleTag("!DOCTYPE",false),DOC_FORMAT); enshrineTag(new SimpleTag("!DOCTYPE",false),DOC_FORMAT);
enshrineTag(new SimpleTag("%",false),SERVER_PAGE); enshrineTag(new SimpleTag("%",false),SERVER_PAGE);
enshrineTag(new SimpleTag("%=",false),SERVER_PAGE); enshrineTag(new SimpleTag("%=",false),SERVER_PAGE);