001 package hirondelle.web4j.ui.tag;
002
003 import java.io.IOException;
004 import javax.servlet.jsp.JspException;
005 import java.util.regex.*;
006 import hirondelle.web4j.ui.tag.TagHelper;
007 import hirondelle.web4j.util.Regex;
008 import hirondelle.web4j.util.Util;
009
010 /**
011 Generate table rows which alternate in appearance, to increase legibility.
012
013 <P>The body of this tag contains one or more <tt>TR</tt> tags. Each <tt>TR</tt> tag contains a
014 <tt>class</tt> attribute, specifying a Cascading Style Sheet class. This tag
015 will simply remove or update the <tt>class</tt> attribute for alternate occurrences of
016 each <tt>TR</tt> tag found in its body.
017
018 <P>If the optional <tt>altClass</tt> attribute is specified, then the <tt>class</tt>
019 attribute of each <tt>TR</tt> is updated to an alternate value, instead of being removed.
020 */
021 public final class AlternatingRow extends TagHelper {
022
023 /**
024 Optional name of a CSS class.
025
026 <P>The CSS class for each <tt>TR</tt> tag found in the body of this tag will be
027 updated to this value, for alternating rows. If this item is not specified, then the <tt>TR</tt>'s
028 class attribute is simply removed instead of updated.
029
030 @param aAltClass must have content.
031 */
032 public void setAltClass(String aAltClass){
033 checkForContent("AltClass", aAltClass);
034 fAltClass = aAltClass;
035 }
036
037 /**
038 For each <tt>TR</tt> tag found in the body, remove or update the <tt>class</tt> attribute.
039
040 <P>If no <tt>altClass</tt> is specified, then the <tt>class</tt> attribute is simply removed entirely.
041 Otherwise, it updated to the <tt>altClass</tt> value.
042 */
043 protected String getEmittedText(String aOriginalBody) throws JspException, IOException {
044 StringBuffer result = new StringBuffer();
045 Matcher matcher = TR_PATTERN.matcher(aOriginalBody);
046 int matchIdx = 0;
047 while (matcher.find()){
048 ++ matchIdx;
049 if( isEvenRow(matchIdx) ){
050 matcher.appendReplacement(result, getReplacement(matcher));
051 }
052 else {
053 String noChange = matcher.group(0);
054 matcher.appendReplacement(result, noChange);
055 }
056 }
057 matcher.appendTail(result);
058 return result.toString();
059 }
060
061 // PRIVATE //
062 private String fAltClass;
063 private static final Pattern TR_PATTERN = Pattern.compile("<tr" + "("+ Regex.ALL_BUT_END_OF_TAG + ")" + "class=" + Regex.QUOTED_ATTR + "("+ Regex.ALL_BUT_END_OF_TAG + ")"+ ">", Pattern.CASE_INSENSITIVE);
064
065 private String getReplacement(Matcher aMatcher){
066 //construct replacement: <TR + G1 + class='G2' + G3 + >
067 StringBuilder result = new StringBuilder("<TR" + aMatcher.group(1));
068 if( Util.textHasContent(fAltClass) ){
069 result.append("class='" + fAltClass + "'");
070 }
071 result.append(aMatcher.group(3) + ">");
072 return result.toString();
073 }
074
075 private boolean isEvenRow(int aMatchIdx){
076 return aMatchIdx % 2 == 0;
077 }
078 }