KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > alfresco > web > ui > repo > component > AbstractItemSelector


1 /*
2  * Copyright (C) 2005 Alfresco, Inc.
3  *
4  * Licensed under the Mozilla Public License version 1.1
5  * with a permitted attribution clause. You may obtain a
6  * copy of the License at
7  *
8  * http://www.alfresco.org/legal/license.txt
9  *
10  * Unless required by applicable law or agreed to in writing,
11  * software distributed under the License is distributed on an
12  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13  * either express or implied. See the License for the specific
14  * language governing permissions and limitations under the
15  * License.
16  */

17 package org.alfresco.web.ui.repo.component;
18
19 import java.io.IOException JavaDoc;
20 import java.util.Collection JavaDoc;
21 import java.util.Map JavaDoc;
22
23 import javax.faces.component.EditableValueHolder;
24 import javax.faces.component.NamingContainer;
25 import javax.faces.component.UIComponent;
26 import javax.faces.component.UIInput;
27 import javax.faces.context.FacesContext;
28 import javax.faces.context.ResponseWriter;
29 import javax.faces.el.ValueBinding;
30 import javax.faces.event.AbortProcessingException;
31 import javax.faces.event.ActionEvent;
32 import javax.faces.event.FacesEvent;
33 import javax.transaction.UserTransaction JavaDoc;
34
35 import org.alfresco.service.cmr.dictionary.DictionaryService;
36 import org.alfresco.service.cmr.repository.NodeRef;
37 import org.alfresco.service.cmr.repository.NodeService;
38 import org.alfresco.web.app.Application;
39 import org.alfresco.web.bean.repository.Repository;
40 import org.alfresco.web.ui.common.Utils;
41 import org.alfresco.web.ui.common.WebResources;
42
43 /**
44  * Abstract component to allow the selection of a hierarchical item
45  *
46  * @author gavinc
47  */

48 public abstract class AbstractItemSelector extends UIInput
49 {
50    private static final String JavaDoc MSG_GO_UP = "go_up";
51    private static final String JavaDoc MSG_OK = "ok";
52    private static final String JavaDoc MSG_CANCEL = "cancel";
53
54    private final static String JavaDoc OK_BUTTON = "_ok";
55    protected final static String JavaDoc OPTION = "_option";
56    
57    protected final static int MODE_BEFORE_SELECTION = 0;
58    protected final static int MODE_INITIAL_SELECTION = 1;
59    protected final static int MODE_DRILLDOWN_SELECTION = 2;
60    protected final static int MODE_CONFIRM_SELECTION = 3;
61    protected final static int MODE_CANCEL_SELECTION = 4;
62    
63    /** label to be displayed before a space is selected */
64    protected String JavaDoc label = null;
65    
66    /** cellspacing between options */
67    protected Integer JavaDoc spacing = null;
68    
69    /** what mode the component is in */
70    protected int mode = MODE_BEFORE_SELECTION;
71    
72    /** currently browsing node id */
73    protected String JavaDoc navigationId = null;
74    
75    /** id of the initially selected item, if value is not set */
76    protected String JavaDoc initialSelectionId = null;
77    
78    /** Flag to show whether the component is disabled */
79    protected Boolean JavaDoc disabled;
80    
81    
82    // ------------------------------------------------------------------------------
83
// Component Impl
84

85    /**
86     * Default constructor
87     */

88    public AbstractItemSelector()
89    {
90       // set the default renderer
91
setRendererType(null);
92    }
93    
94    /**
95     * @see javax.faces.component.UIComponent#getFamily()
96     */

97    public abstract String JavaDoc getFamily();
98    
99    /**
100     * Retrieves the default label to show if none has been defined and nothing has been selected
101     *
102     * @return Default label
103     */

104    public abstract String JavaDoc getDefaultLabel();
105    
106    /**
107     * Retrieves the id of the parent node of the current navigation node
108     *
109     * @param context The Faces context
110     * @return Id of the parent node or null if the parent is the root
111     */

112    public abstract String JavaDoc getParentNodeId(FacesContext context);
113    
114    /**
115     * Returns a collection of child associations for the current navigation node
116     *
117     * @param context The Faces context
118     * @return The children
119     */

120    public abstract Collection JavaDoc<NodeRef> getChildrenForNode(FacesContext context);
121    
122    /**
123     * Returns a collection of child associations of the root
124     *
125     * @param context The Faces context
126     * @return The root options
127     */

128    public abstract Collection JavaDoc<NodeRef> getRootChildren(FacesContext context);
129    
130    /**
131     * @return The icon image to display next to the item links, or null for no icon
132     */

133    public abstract String JavaDoc getItemIcon();
134    
135    /**
136     * @see javax.faces.component.StateHolder#restoreState(javax.faces.context.FacesContext, java.lang.Object)
137     */

138    public void restoreState(FacesContext context, Object JavaDoc state)
139    {
140       Object JavaDoc values[] = (Object JavaDoc[])state;
141       // standard component attributes are restored by the super class
142
super.restoreState(context, values[0]);
143       this.label = (String JavaDoc)values[1];
144       this.spacing = (Integer JavaDoc)values[2];
145       this.mode = ((Integer JavaDoc)values[3]).intValue();
146       this.navigationId = (String JavaDoc)values[4];
147       this.initialSelectionId = (String JavaDoc)values[5];
148       this.disabled = (Boolean JavaDoc)values[6];
149    }
150    
151    /**
152     * @see javax.faces.component.StateHolder#saveState(javax.faces.context.FacesContext)
153     */

154    public Object JavaDoc saveState(FacesContext context)
155    {
156       Object JavaDoc values[] = new Object JavaDoc[7];
157       // standard component attributes are saved by the super class
158
values[0] = super.saveState(context);
159       values[1] = this.label;
160       values[2] = this.spacing;
161       values[3] = this.mode;
162       values[4] = this.navigationId;
163       values[5] = this.initialSelectionId;
164       values[6] = this.disabled;
165       return (values);
166    }
167    
168    /**
169     * @see javax.faces.component.UIComponentBase#decode(javax.faces.context.FacesContext)
170     */

171    public void decode(FacesContext context)
172    {
173       Map JavaDoc requestMap = context.getExternalContext().getRequestParameterMap();
174       String JavaDoc fieldId = getHiddenFieldName();
175       String JavaDoc value = (String JavaDoc)requestMap.get(fieldId);
176       
177       int mode = this.mode;
178       if (value != null && value.length() != 0)
179       {
180          // break up the submitted value into it's parts
181

182          // first part is the mode the component is in
183
// followed by the id of the selection if we are drilling down
184
String JavaDoc id = null;
185          int sepIndex = value.indexOf(NamingContainer.SEPARATOR_CHAR);
186          if (sepIndex != -1)
187          {
188             mode = Integer.parseInt(value.substring(0, sepIndex));
189             if (value.length() > sepIndex + 1)
190             {
191                id = value.substring(sepIndex + 1);
192             }
193          }
194          else
195          {
196             mode = Integer.parseInt(value);
197          }
198          
199          // raise an event so we can pick the changed values up later
200
ItemSelectorEvent event = new ItemSelectorEvent(this, mode, id);
201          this.queueEvent(event);
202       }
203       
204       if (mode == MODE_CONFIRM_SELECTION)
205       {
206          // only bother to check the selection if the mode is set to MODE_AFTER_SELECTION
207
// see if a selection has been submitted
208
String JavaDoc selection = (String JavaDoc)requestMap.get(getClientId(context) + OPTION);
209          if (selection != null && selection.length() != 0)
210          {
211             ((EditableValueHolder)this).setSubmittedValue(new NodeRef(Repository.getStoreRef(), selection));
212          }
213       }
214    }
215    
216    /**
217     * @see javax.faces.component.UIInput#broadcast(javax.faces.event.FacesEvent)
218     */

219    public void broadcast(FacesEvent event) throws AbortProcessingException
220    {
221       if (event instanceof ItemSelectorEvent)
222       {
223          ItemSelectorEvent spaceEvent = (ItemSelectorEvent)event;
224          this.mode = spaceEvent.Mode;
225          this.navigationId = spaceEvent.Id;
226       }
227       else
228       {
229          super.broadcast(event);
230       }
231    }
232
233    /**
234     * @see javax.faces.component.UIComponentBase#encodeBegin(javax.faces.context.FacesContext)
235     */

236    public void encodeBegin(FacesContext context) throws IOException JavaDoc
237    {
238       if (isRendered() == false)
239       {
240          return;
241       }
242       
243       NodeService service = getNodeService(context);
244       
245       if (isDisabled())
246       {
247          // render a read-only view of the selected category (if any)
248
ResponseWriter out = context.getResponseWriter();
249
250          // see if there is a current value for the category
251
NodeRef nodeRef = (NodeRef)getSubmittedValue();
252          if (nodeRef == null)
253          {
254             Object JavaDoc val = getValue();
255             if (val instanceof NodeRef)
256             {
257                nodeRef = (NodeRef)val;
258             }
259             else if (val instanceof String JavaDoc && ((String JavaDoc)val).length() != 0)
260             {
261                nodeRef = new NodeRef((String JavaDoc)val);
262             }
263          }
264          
265          // if there is a value show it's name
266
if (nodeRef != null)
267          {
268             out.write(Repository.getNameForNode(service, nodeRef));
269          }
270       }
271       else
272       {
273          // render an editable control for selecting items
274
String JavaDoc clientId = getClientId(context);
275          
276          StringBuilder JavaDoc buf = new StringBuilder JavaDoc(512);
277          Map JavaDoc attrs = this.getAttributes();
278          boolean showValueInHiddenField = false;
279          NodeRef value = null;
280          
281          String JavaDoc image = null;
282          if (getItemIcon() != null)
283          {
284             image = "<span style='padding-right:4px'>" + Utils.buildImageTag(context, getItemIcon(), null, "absmiddle") + "</span>";
285          }
286          
287          switch (this.mode)
288          {
289             case MODE_BEFORE_SELECTION:
290             case MODE_CONFIRM_SELECTION:
291             case MODE_CANCEL_SELECTION:
292             {
293                UserTransaction JavaDoc tx = null;
294                try
295                {
296                   tx = Repository.getUserTransaction(context, true);
297                   tx.begin();
298                   
299                   NodeRef submittedValue = (NodeRef)getSubmittedValue();
300                   if (submittedValue != null)
301                   {
302                      value = submittedValue;
303                   }
304                   else
305                   {
306                      Object JavaDoc val = getValue();
307                      if (val instanceof NodeRef)
308                      {
309                         value = (NodeRef)val;
310                      }
311                      else if (val instanceof String JavaDoc && ((String JavaDoc)val).length() != 0)
312                      {
313                         value = new NodeRef((String JavaDoc)val);
314                      }
315                   }
316                   
317                   // show just the initial or current selection link
318
String JavaDoc label;
319                   if (value == null)
320                   {
321                      label = getLabel();
322                      
323                      // if the label is still null get the default from the message bundle
324
if (label == null)
325                      {
326                         label = getDefaultLabel();
327                      }
328                   }
329                   else
330                   {
331                      label = Repository.getNameForNode(service, value);
332                      showValueInHiddenField = true;
333                   }
334                   
335                   // output surrounding span for style purposes
336
buf.append("<span");
337                   if (attrs.get("style") != null)
338                   {
339                      buf.append(" style=\"")
340                         .append(attrs.get("style"))
341                         .append('"');
342                   }
343                   if (attrs.get("styleClass") != null)
344                   {
345                      buf.append(" class=")
346                         .append(attrs.get("styleClass"));
347                   }
348                   buf.append(">");
349                   
350                   // rendering as initial selection mode means the sibilings of the selected
351
// item are shown instead of the children on first click in.
352
int theMode = MODE_INITIAL_SELECTION;
353                   
354                   // if we have an initial selection and no value set the initial one up
355
if (value == null && this.getInitialSelection() != null)
356                   {
357                      value = new NodeRef(Repository.getStoreRef(), this.getInitialSelection());
358                   }
359                   
360                   // field value is whether we are picking and the current or parent Id value
361
String JavaDoc fieldValue;
362                   if (value != null)
363                   {
364                      fieldValue = encodeFieldValues(theMode, value.getId());
365                   }
366                   else
367                   {
368                      fieldValue = encodeFieldValues(theMode, null);
369                   }
370                   buf.append("<a HREF='#' onclick=\"");
371                   buf.append(Utils.generateFormSubmit(context, this, getHiddenFieldName(), fieldValue));
372                   buf.append('"');
373                   if (attrs.get("nodeStyle") != null)
374                   {
375                      buf.append(" style=\"")
376                         .append(attrs.get("nodeStyle"))
377                         .append('"');
378                   }
379                   if (attrs.get("nodeStyleClass") != null)
380                   {
381                      buf.append(" class=")
382                         .append(attrs.get("nodeStyleClass"));
383                   }
384                   buf.append(">")
385                      .append(label)
386                      .append("</a></span>");
387                   
388                   tx.commit();
389                }
390                catch (Throwable JavaDoc err)
391                {
392                   try { if (tx != null) {tx.rollback();} } catch (Exception JavaDoc tex) {}
393                   Utils.addErrorMessage(err.getMessage(), err);
394                }
395                
396                break;
397             }
398             
399             case MODE_DRILLDOWN_SELECTION:
400             case MODE_INITIAL_SELECTION:
401             {
402                // show the picker list
403
// get the children of the node ref to show
404
buf.append("<table border=0 cellspacing=1 cellpadding=1");
405                if (attrs.get("style") != null)
406                {
407                   buf.append(" style=\"")
408                      .append(attrs.get("style"))
409                      .append('"');
410                }
411                if (attrs.get("styleClass") != null)
412                {
413                   buf.append(" class=")
414                      .append(attrs.get("styleClass"));
415                }
416                buf.append(">");
417                
418                // if we are setting up the initial selection we need to get the
419
// parent id of the initial selection so the user can actually see
420
// the item when the list is rendered
421
if (this.mode == MODE_INITIAL_SELECTION)
422                {
423                   this.navigationId = getParentNodeId(context);
424                }
425                
426                // render "Go Up" link if not at the root level
427
if (this.navigationId != null)
428                {
429                   // get the id of the parent node of the current navigation node,
430
// null indicates we are at the root level
431
String JavaDoc id = getParentNodeId(context);
432                   
433                   buf.append("<tr><td></td><td>");
434                   
435                   String JavaDoc upImage = Utils.buildImageTag(context, WebResources.IMAGE_GO_UP, null, "absmiddle");
436                   
437                   // render a link to the parent node
438
renderNodeLink(context, id, Application.getMessage(context, MSG_GO_UP), upImage, buf);
439                   buf.append("</td></tr>");
440                }
441                
442                String JavaDoc okButtonId = clientId + OK_BUTTON;
443                boolean okButtonEnabled = false;
444                
445                // display the children of the specified navigation node ID
446
Collection JavaDoc<NodeRef> childRefs;
447                if (this.navigationId != null)
448                {
449                   // get a list of children for the current navigation node
450
childRefs = getChildrenForNode(context);
451                }
452                else
453                {
454                   // no node set - special case to show the initial root items
455
childRefs = getRootChildren(context);
456                }
457                
458                UserTransaction JavaDoc tx = null;
459                try
460                {
461                   tx = Repository.getUserTransaction(context, true);
462                   tx.begin();
463                   
464                   for (NodeRef childRef : childRefs)
465                   {
466                      // render each child found
467
String JavaDoc childId = childRef.getId();
468                      buf.append("<tr><td><input type='radio' name='")
469                      .append(clientId).append(OPTION).append("' value='")
470                      .append(childId).append("'");
471                      if (childId.equals(this.initialSelectionId))
472                      {
473                         buf.append(" checked");
474                         
475                         // if any radio buttons are checked, the OK button must start enabled
476
okButtonEnabled = true;
477                         
478                         // now remove the initial selection as we only need it the first time
479
this.initialSelectionId = null;
480                      }
481                      buf.append(" onclick=\"javascript:document.getElementById('")
482                         .append(okButtonId)
483                         .append("').disabled=false;\"");
484                      buf.append("/></td><td>");
485                      
486                      // get the name for the child and output as link
487
NodeRef childNodeRef = new NodeRef(Repository.getStoreRef(), childId);
488                      String JavaDoc name = Repository.getNameForNode(service, childNodeRef);
489                      renderNodeLink(context, childId, name, image, buf);
490                      buf.append("</td></tr>");
491                   }
492                   
493                   // render OK button
494
String JavaDoc fieldValue = encodeFieldValues(MODE_CONFIRM_SELECTION, null);
495                   buf.append("<tr style='padding-top:4px'><td></td><td align=center>")
496                      .append("<input type='button' ")
497                      .append(okButtonEnabled == false ? "disabled" : "")
498                      .append(" onclick=\"")
499                      .append(Utils.generateFormSubmit(context, this, getHiddenFieldName(), fieldValue))
500                      .append("\" value='")
501                      .append(Application.getMessage(context, MSG_OK))
502                      .append("' id='")
503                      .append(okButtonId)
504                      .append("'>&nbsp;");
505                   
506                   // render Cancel button
507
fieldValue = encodeFieldValues(MODE_CANCEL_SELECTION, null);
508                   buf.append("<input type='button' onclick=\"")
509                      .append(Utils.generateFormSubmit(context, this, getHiddenFieldName(), fieldValue))
510                      .append("\" value='")
511                      .append(Application.getMessage(context, MSG_CANCEL))
512                      .append("'></td></tr>");
513                   
514                   buf.append("</table>");
515                   
516                   tx.commit();
517                }
518                catch (Throwable JavaDoc err)
519                {
520                   try { if (tx != null) {tx.rollback();} } catch (Exception JavaDoc tex) {}
521                   throw new RuntimeException JavaDoc(err);
522                }
523                
524                break;
525             }
526          }
527          
528          // output a hidden field containing the currently selected NodeRef so that JavaScript
529
// can be used to check the state of the component
530
buf.append("<input type='hidden' name='");
531          buf.append(clientId);
532          buf.append("_selected' id='");
533          buf.append(clientId);
534          buf.append("_selected' value='");
535          if (showValueInHiddenField)
536          {
537             buf.append(value);
538          }
539          buf.append("'/>");
540          
541          context.getResponseWriter().write(buf.toString());
542       }
543    }
544    
545    
546    // ------------------------------------------------------------------------------
547
// Strongly typed component property accessors
548

549    /**
550     * @return Returns the label.
551     */

552    public String JavaDoc getLabel()
553    {
554       ValueBinding vb = getValueBinding("label");
555       if (vb != null)
556       {
557          this.label = (String JavaDoc)vb.getValue(getFacesContext());
558       }
559       
560       return this.label;
561    }
562    
563    /**
564     * @param label The label to set.
565     */

566    public void setLabel(String JavaDoc label)
567    {
568       this.label = label;
569    }
570
571    /**
572     * @return Returns the cell spacing value between space options. Default is 2.
573     */

574    public Integer JavaDoc getSpacing()
575    {
576       ValueBinding vb = getValueBinding("spacing");
577       if (vb != null)
578       {
579          this.spacing = (Integer JavaDoc)vb.getValue(getFacesContext());
580       }
581       
582       if (this.spacing != null)
583       {
584          return this.spacing.intValue();
585       }
586       else
587       {
588          // return default
589
return 2;
590       }
591    }
592    
593    /**
594     * @param spacing The spacing to set.
595     */

596    public void setSpacing(Integer JavaDoc spacing)
597    {
598       this.spacing = spacing;
599    }
600    
601    /**
602     * @return Returns the initial selecttion.
603     */

604    public String JavaDoc getInitialSelection()
605    {
606       ValueBinding vb = getValueBinding("initialSelection");
607       if (vb != null)
608       {
609          this.initialSelectionId = (String JavaDoc)vb.getValue(getFacesContext());
610       }
611       
612       return this.initialSelectionId;
613    }
614    
615    /**
616     * @param initialSelection The initial selection to set.
617     */

618    public void setInitialSelection(String JavaDoc initialSelection)
619    {
620       this.initialSelectionId = initialSelection;
621    }
622    
623    /**
624     * Determines whether the component should be rendered in a disabled state
625     *
626     * @return Returns whether the component is disabled
627     */

628    public boolean isDisabled()
629    {
630       if (this.disabled == null)
631       {
632          ValueBinding vb = getValueBinding("disabled");
633          if (vb != null)
634          {
635             this.disabled = (Boolean JavaDoc)vb.getValue(getFacesContext());
636          }
637       }
638       
639       if (this.disabled == null)
640       {
641          this.disabled = Boolean.FALSE;
642       }
643       
644       return this.disabled;
645    }
646
647    /**
648     * Determines whether the component should be rendered in a disabled state
649     *
650     * @param disabled true to disable the component
651     */

652    public void setDisabled(boolean disabled)
653    {
654       this.disabled = disabled;
655    }
656    
657    
658    // ------------------------------------------------------------------------------
659
// Protected helpers
660

661    /**
662     * We use a unique hidden field name based on our client Id.
663     * This is on the assumption that there won't be many selectors on screen at once!
664     * Also means we have less values to decode on submit.
665     *
666     * @return hidden field name
667     */

668    protected String JavaDoc getHiddenFieldName()
669    {
670       return this.getClientId(getFacesContext());
671    }
672    
673    protected String JavaDoc encodeFieldValues(int mode, String JavaDoc id)
674    {
675       if (id != null)
676       {
677          return Integer.toString(mode) + NamingContainer.SEPARATOR_CHAR + id;
678       }
679       else
680       {
681          return Integer.toString(mode);
682       }
683    }
684    
685    /**
686     * Render a node descendant as a clickable link
687     *
688     * @param context FacesContext
689     * @param childRef The ChildAssocRef of the child to render an HTML link for
690     *
691     * @return HTML for a descendant link
692     */

693    protected String JavaDoc renderNodeLink(FacesContext context, String JavaDoc id, String JavaDoc name, String JavaDoc prefix, StringBuilder JavaDoc buf)
694    {
695       buf.append("<a HREF='#' onclick=\"");
696       String JavaDoc fieldValue = encodeFieldValues(MODE_DRILLDOWN_SELECTION, id);
697       buf.append(Utils.generateFormSubmit(context, this, getHiddenFieldName(), fieldValue));
698       buf.append('"');
699       Map JavaDoc attrs = this.getAttributes();
700       if (attrs.get("nodeStyle") != null)
701       {
702          buf.append(" style=\"")
703             .append(attrs.get("nodeStyle"))
704             .append('"');
705       }
706       if (attrs.get("nodeStyleClass") != null)
707       {
708          buf.append(" class=")
709             .append(attrs.get("nodeStyleClass"));
710       }
711       buf.append('>');
712       
713       if (prefix != null)
714       {
715          buf.append(prefix);
716       }
717       buf.append(Utils.encode(name));
718       
719       buf.append("</a>");
720       
721       return buf.toString();
722    }
723    
724    /**
725     * Use Spring JSF integration to return the Node Service bean instance
726     *
727     * @param context FacesContext
728     *
729     * @return Node Service bean instance or throws exception if not found
730     */

731    protected static NodeService getNodeService(FacesContext context)
732    {
733       NodeService service = Repository.getServiceRegistry(context).getNodeService();
734       if (service == null)
735       {
736          throw new IllegalStateException JavaDoc("Unable to obtain NodeService bean reference.");
737       }
738       
739       return service;
740    }
741    
742    /**
743     * Use Spring JSF integration to return the Dictionary Service bean instance
744     *
745     * @param context FacesContext
746     *
747     * @return Dictionary Service bean instance or throws exception if not found
748     */

749    protected static DictionaryService getDictionaryService(FacesContext context)
750    {
751       DictionaryService service = Repository.getServiceRegistry(context).getDictionaryService();
752       if (service == null)
753       {
754          throw new IllegalStateException JavaDoc("Unable to obtain DictionaryService bean reference.");
755       }
756       
757       return service;
758    }
759    
760    /**
761     * Class representing the clicking of a breadcrumb element.
762     */

763    public static class ItemSelectorEvent extends ActionEvent
764    {
765       public ItemSelectorEvent(UIComponent component, int mode, String JavaDoc id)
766       {
767          super(component);
768          Mode = mode;
769          Id = id;
770       }
771       
772       public int Mode;
773       private String JavaDoc Id;
774    }
775 }
776
Popular Tags