View Javadoc

1   /**
2    * Licensed under the Artistic License; you may not use this file
3    * except in compliance with the License.
4    * You may obtain a copy of the License at
5    *
6    *      http://displaytag.sourceforge.net/license.html
7    *
8    * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
9    * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
10   * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
11   */
12  package org.displaytag.model;
13  
14  import java.io.UnsupportedEncodingException;
15  import java.net.URLEncoder;
16  
17  import org.apache.commons.lang.ObjectUtils;
18  import org.apache.commons.lang.StringUtils;
19  import org.apache.commons.lang.UnhandledException;
20  import org.apache.commons.lang.builder.ToStringBuilder;
21  import org.apache.commons.lang.builder.ToStringStyle;
22  import org.displaytag.decorator.DisplaytagColumnDecorator;
23  import org.displaytag.exception.DecoratorException;
24  import org.displaytag.exception.ObjectLookupException;
25  import org.displaytag.util.Anchor;
26  import org.displaytag.util.Href;
27  import org.displaytag.util.HtmlAttributeMap;
28  import org.displaytag.util.HtmlTagUtil;
29  import org.displaytag.util.LookupUtil;
30  import org.displaytag.util.TagConstants;
31  
32  
33  /**
34   * Represents a column in a table.
35   * @author Fabrizio Giustina
36   * @version $Revision: 1092 $ ($Author: rapruitt $)
37   */
38  public class Column
39  {
40  
41      /**
42       * Row this column belongs to.
43       */
44      private Row row;
45  
46      /**
47       * Header of this column. The header cell contains all the attributes common to all cells in the same column
48       */
49      private HeaderCell header;
50  
51      /**
52       * copy of the attribute map from the header cell. Needed to change attributes (title) in this cell only
53       */
54      private HtmlAttributeMap htmlAttributes;
55  
56      /**
57       * contains the evaluated body value. Filled in getOpenTag.
58       */
59      private String stringValue;
60  
61      /**
62       * Cell.
63       */
64      private Cell cell;
65  
66      /**
67       * Constructor for Column.
68       * @param headerCell HeaderCell
69       * @param currentCell Cell
70       * @param parentRow Row
71       */
72      public Column(HeaderCell headerCell, Cell currentCell, Row parentRow)
73      {
74          this.header = headerCell;
75          this.row = parentRow;
76          this.cell = currentCell;
77  
78          // also copy html attributes
79          this.htmlAttributes = headerCell.getHtmlAttributes();
80      }
81  
82      /**
83       * Get the header cell for this column.
84       * @return the cell
85       */
86      public HeaderCell getHeaderCell()
87      {
88          return this.header;
89      }
90  
91      /**
92       * Gets the value, after calling the table / column decorator is requested.
93       * @param decorated boolean
94       * @return Object will never be null if ShowNulls has been set to false
95       * @throws ObjectLookupException for errors in bean property lookup
96       * @throws DecoratorException if a column decorator is used and an exception is thrown during value decoration
97       */
98      public Object getValue(boolean decorated) throws ObjectLookupException, DecoratorException
99      {
100 
101         Object object = null;
102 
103         // a static value has been set?
104         if (this.cell.getStaticValue() != null)
105         {
106             object = this.cell.getStaticValue();
107         }
108         else if (this.header.getBeanPropertyName() != null)
109         {
110 
111             // if a decorator has been set, and if decorator has a getter for the requested property only, check
112             // decorator
113             if (decorated
114                 && this.row.getParentTable().getTableDecorator() != null
115                 && this.row.getParentTable().getTableDecorator().hasGetterFor(this.header.getBeanPropertyName()))
116             {
117 
118                 object = LookupUtil.getBeanProperty(this.row.getParentTable().getTableDecorator(), this.header
119                     .getBeanPropertyName());
120             }
121             else
122             {
123                 // else check underlining object
124                 object = LookupUtil.getBeanProperty(this.row.getObject(), this.header.getBeanPropertyName());
125             }
126         }
127 
128         DisplaytagColumnDecorator[] decorators = this.header.getColumnDecorators();
129         if (decorated)
130         {
131             for (int j = 0; j < decorators.length; j++)
132             {
133                 object = decorators[j].decorate(object, row.getParentTable().getPageContext(), row
134                     .getParentTable()
135                     .getMedia());
136             }
137         }
138 
139         if (object == null || "null".equals(object)) //$NON-NLS-1$
140         {
141             if (!this.header.getShowNulls())
142             {
143                 object = TagConstants.EMPTY_STRING;
144             }
145         }
146 
147         return object;
148     }
149 
150     /**
151      * Generates the cell open tag.
152      * @return String td open tag
153      */
154     public String getOpenTag()
155     {
156         HtmlAttributeMap rowAttributes = cell.getPerRowAttributes();
157 
158         HtmlAttributeMap atts = htmlAttributes;
159         if (rowAttributes != null)
160         {
161             atts = (HtmlAttributeMap) atts.clone();
162             atts.putAll(rowAttributes);
163         }
164         return HtmlTagUtil.createOpenTagString(TagConstants.TAGNAME_COLUMN, atts);
165     }
166 
167     /**
168      * Initialize the cell value.
169      * @throws ObjectLookupException for errors in bean property lookup
170      * @throws DecoratorException if a column decorator is used and an exception is thrown during value decoration
171      * @throws DecoratorException
172      * @throws ObjectLookupException
173      */
174     public void initialize() throws DecoratorException, ObjectLookupException
175     {
176         if (this.stringValue == null)
177         {
178             this.stringValue = createChoppedAndLinkedValue();
179         }
180     }
181 
182     /**
183      * Generates the cell close tag (&lt;/td>).
184      * @return String td closing tag
185      */
186     public String getCloseTag()
187     {
188         this.stringValue = null;
189         return this.header.getCloseTag();
190     }
191 
192     /**
193      * Calculates the cell content, cropping or linking the value as needed.
194      * @return String
195      * @throws ObjectLookupException for errors in bean property lookup
196      * @throws DecoratorException if a column decorator is used and an exception is thrown during value decoration
197      */
198     public String createChoppedAndLinkedValue() throws ObjectLookupException, DecoratorException
199     {
200 
201         String fullValue = ObjectUtils.toString(getValue(true));
202         String choppedValue;
203 
204         // trim the string if a maxLength or maxWords is defined
205         if (this.header.getMaxLength() > 0)
206         {
207             choppedValue = HtmlTagUtil.abbreviateHtmlString(fullValue, this.header.getMaxLength(), false);
208         }
209         else if (this.header.getMaxWords() > 0)
210         {
211             choppedValue = HtmlTagUtil.abbreviateHtmlString(fullValue, this.header.getMaxWords(), true);
212         }
213         else
214         {
215             choppedValue = fullValue;
216         }
217 
218         // chopped content? add the full content to the column "title" attribute
219         // note, simply checking that length is less than before can't be enough due to the "..." added if the string is
220         // cropped
221         if (!ObjectUtils.equals(fullValue, choppedValue))
222         {
223             // clone the attribute map, don't want to add title to all the columns
224             this.htmlAttributes = (HtmlAttributeMap) this.htmlAttributes.clone();
225             // add title
226             this.htmlAttributes.put(TagConstants.ATTRIBUTE_TITLE, HtmlTagUtil.stripHTMLTags(fullValue));
227         }
228 
229         if (this.header.getHref() != null)
230         {
231             // generates the href for the link
232             Href colHref = getColumnHref(fullValue);
233             Anchor anchor = new Anchor(colHref, choppedValue);
234             choppedValue = anchor.toString();
235         }
236 
237         return choppedValue;
238     }
239 
240     /**
241      * Generates the href for the column using paramName/property/scope.
242      * @param columnContent column body
243      * @return generated Href
244      * @throws ObjectLookupException for errors in lookin up object properties
245      */
246     private Href getColumnHref(String columnContent) throws ObjectLookupException
247     {
248         // copy href
249         Href colHref = (Href) this.header.getHref().clone();
250 
251         // do we need to add a param?
252         if (this.header.getParamName() != null)
253         {
254 
255             Object paramValue;
256 
257             if (this.header.getParamProperty() != null)
258             {
259                 // different property, go get it
260                 paramValue = LookupUtil.getBeanProperty(this.row.getObject(), this.header.getParamProperty());
261 
262             }
263             else
264             {
265                 // same property as content
266                 paramValue = columnContent;
267             }
268 
269             if (paramValue != null)
270             {
271                 try
272                 {
273                     colHref.addParameter(this.header.getParamName(), URLEncoder.encode(
274                         paramValue.toString(),
275                         StringUtils.defaultString(this.row.getParentTable().getEncoding(), "UTF8"))); //$NON-NLS-1$
276                 }
277                 catch (UnsupportedEncodingException e)
278                 {
279                     throw new UnhandledException(e);
280                 }
281             }
282         }
283         return colHref;
284     }
285 
286     /**
287      * get the final value to be displayed in the table. This method can only be called after initialize(), where the
288      * content is evaluated
289      * @return String final value to be displayed in the table
290      */
291     public String getChoppedAndLinkedValue()
292     {
293         return this.stringValue;
294     }
295 
296     /**
297      * @see java.lang.Object#toString()
298      */
299     public String toString()
300     {
301         return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) //
302             .append("cell", this.cell) //$NON-NLS-1$
303             .append("header", this.header) //$NON-NLS-1$
304             .append("htmlAttributes", this.htmlAttributes) //$NON-NLS-1$
305             .append("stringValue", this.stringValue) //$NON-NLS-1$
306             .toString();
307     }
308 }