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 }