View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.trinidad.menu;
20  
21  import java.util.List;
22  
23  import java.util.Map;
24  
25  import javax.faces.context.FacesContext;
26  
27  import org.apache.myfaces.trinidad.model.XMLMenuModel;
28  import org.apache.myfaces.trinidad.util.ContainerUtils;
29  
30  /**
31   * Code generic to a Menu Nodes of the menu model.
32   *
33   * IMPORTANT NOTE: even internally, values that support EL expressions
34   * should use the "get" methods to obtain values.
35   *
36   */
37  
38  public class MenuNode
39  {
40    /**
41      * Constructs a MenuNode
42      */
43    public MenuNode()
44    {
45    }
46  
47    /**
48      * Set the menu item's label.
49      *
50      * @param label - String name shown in the menu item
51      */
52    public void setLabel(String label)
53    {
54      _label = label;
55    }
56  
57    /**
58      * Get the menu item's label
59      *
60      * This could either be a string value set directly in
61      * the metadata or an EL expression string. In the case
62      * of an EL expression, we need to get its bound value.
63      *
64      * @return label as a String
65      */
66    public String getLabel()
67    {
68      if (_bundleKey != null && _bundleName != null)
69      {
70        // Load the resource bundle based on the locale of the
71        // current request.  If the locale has not changed, this
72        // method just returns.
73        MenuUtils.loadBundle(_bundleName, _bundleKey + getHandlerId());
74      }
75  
76      if (   _label != null
77          && ContainerUtils.isValueReference(_label)
78         )
79      {
80        // do not set _label to the evaluated EL.
81        // It may change at times in the EL.
82        return _evalElStr(_label);
83      }
84      return _label;
85    }
86  
87    /**
88      * Set the icon used by the menu item.
89      *
90      * @param icon - the String URI to the icon.
91      */
92    public void setIcon(String icon)
93    {
94      _icon = icon;
95    }
96  
97    /**
98      * Get the icon used by the menu item
99      * This could either be a string value set directly in
100     * the metadata or an EL expression string. In the case
101     * of an EL expression, we need to get its bound value.
102     *
103     * @return icon - the String URI to the icon.
104     */
105   public String getIcon()
106   {
107     return MenuUtils.evalString(_icon);
108   }
109 
110   /**
111     * Sets the rendered attribute of the menu item.
112     * If false, menu item will not appear.
113     *
114     * @param rendered - boolean that toggles the visible state of the XMLMenuModel
115     * item.
116     */
117   public void setRendered(boolean rendered)
118   {
119     _renderedStr = rendered ? "true" : "false";
120   }
121 
122   /**
123     * Gets the rendered attribute of the menu item.
124     * If false, menu item will not appear.
125     *
126     * @return boolean indicating whether or not the menu item is visible.
127     */
128   public boolean getRendered()
129   {
130     boolean rendered = MenuUtils.evalBoolean(_renderedStr, true);
131     return rendered;
132   }
133 
134   /**
135     * Sets the disabled attribute of the menu item.
136     * If true, menu item will not appear greyed-out and clicking
137     * on it will have no effect
138     *
139     * @param disabled - boolean that toggles the enabled/disabled state of the
140     * menu item.
141     */
142   public void setDisabled(boolean disabled)
143   {
144     _disabledStr = disabled ? "true" : "false";
145   }
146 
147   /**
148     * Gets the disabled attribute of the menu item.
149     * If true, menu item will not appear greyed-out and clicking
150     * on it will have no effect
151     *
152     * @return boolean indicating whether or not the menu item is disabled.
153     */
154   public boolean getDisabled()
155   {
156     boolean disabled = MenuUtils.evalBoolean(_disabledStr, false);
157     return disabled;
158   }
159 
160   /**
161     * Sets the visible attribute of the menu item.
162     * If false, menu item will not appear
163     *
164     * @param visible - boolean that toggles the visible state of the
165     * menu item.
166     */
167   public void setVisible(boolean visible)
168   {
169     _visibleStr = visible ? "true" : "false";
170   }
171 
172   /**
173     * Gets the visible attribute of the menu item.
174     * If false, menu item will not appear
175     *
176     * @return boolean indicating whether or not the menu item is visible.
177     */
178   public boolean getVisible()
179   {
180     boolean visible = MenuUtils.evalBoolean(_visibleStr, true);
181     return visible;
182   }
183 
184   /**
185     * Sets the defaultFocusPath attribute of the menu item.
186     *
187     * @param defaultFocusPath - boolean that tells the XMLMenuModel model that
188     * the focus path to this node should be used in cases where the focus path
189     * is not determinable by the XMLMenuModel model.
190     */
191   public void setDefaultFocusPath(boolean defaultFocusPath)
192   {
193     _defaultFocusPathStr = defaultFocusPath ? "true" : "false";
194   }
195 
196   /**
197     * Gets the defaultFocusPath attribute of the menu item.
198     *
199     * @return boolean indicating whether or not this is the focus path to use,
200     * by default, in cases where there are duplicate paths to this node and
201     * the focus path is not determinable by the XMLMenuModel model.
202     */
203   public boolean getDefaultFocusPath()
204   {
205     boolean defaultFocusPath = MenuUtils.evalBoolean(_defaultFocusPathStr,
206                                                      false);
207     return defaultFocusPath;
208   }
209 
210   /**
211     * Get the List of menu item's children.
212     *
213     * @return List of menu item's children
214     */
215   public List<MenuNode> getChildren()
216   {
217     return _children;
218   }
219 
220   /**
221     * Set the List of menu item's children.
222     *
223     * @param children - List of MenuNode children for this MenuNode
224     */
225   public void setChildren(List<MenuNode> children)
226   {
227     _children = children;
228   }
229   
230 
231   /**
232     * Gets the readOnly state of the node.
233     *
234     * @return the node's readOnly state as a boolean.
235     */
236   public boolean getReadOnly()
237   {
238     boolean readOnly = MenuUtils.evalBoolean(_readOnlyStr, false);
239     return readOnly;
240   }
241 
242   /**
243     * Sets the the value of the readOnly attribute of the node.
244     *
245     * @param readOnly - boolean setting readOnly state of the node
246     */
247   public void setReadOnly(boolean readOnly)
248   {
249     _readOnlyStr = readOnly ? "true" : "false";
250   }
251 
252   /**
253   * Sets the value of the node's focusViewId property.
254   *
255   * @param focusViewId - string value of the Node's "focusViewId" property.
256   */
257   public void setFocusViewId(String focusViewId)
258   {
259     _focusViewId = focusViewId;
260   }
261 
262   /**
263   * Gets the value of the node's focusViewId property.
264   *
265   * @return string - the value of the Node's "focusViewId" property.
266   */
267   public String getFocusViewId()
268   {
269     return _focusViewId;
270   }
271 
272   /**
273     * Sets the rendered attribute of the menu item.
274     * If false, menu item will not appear.
275     *
276     * Called only from MenuContentHandlerImpl during parsing of metadata.
277     *
278     * @param renderedStr - string representing a boolean value
279     */
280   public void setRendered(String renderedStr)
281   {
282     _renderedStr = renderedStr;
283   }
284 
285   /**
286     * Sets the disabled attribute of the menu item.
287     * If false, menu item will appear in a disabled state.
288     *
289     * @param disabledStr - string representing a boolean value or
290     * an EL Expression
291     */
292   public void setDisabled(String disabledStr)
293   {
294     _disabledStr = disabledStr;
295   }
296 
297   /**
298     * Sets the readOnly attribute of the menu item.
299     * If false, menu item will appear in a readOnly state.
300     *
301     * @param readOnlyStr - string representing a boolean value or EL
302     * expression.
303     */
304   public void setReadOnly(String readOnlyStr)
305   {
306     _readOnlyStr = readOnlyStr;
307   }
308 
309   /**
310     * Sets the visible attribute of the menu item.
311     * If false, menu item will not appear.
312     *
313     * @param visibleStr - string representing a boolean value or
314     * an EL Expression
315     */
316   public void setVisible(String visibleStr)
317   {
318     _visibleStr = visibleStr;
319   }
320 
321   /**
322     * Sets the defaultFocusPath attribute of the menu item.
323     *
324     * Called only from MenuContentHandlerImpl during parsing of metadata.
325     *
326     * @param defaultFocusPathStr - string representing a boolean value
327     */
328   public void setDefaultFocusPath(String defaultFocusPathStr)
329   {
330     _defaultFocusPathStr = defaultFocusPathStr;
331   }
332 
333   /**
334    * setAccessKey - Takes either a single character String or
335    * an EL expression and sets the value of the accessKey attribute
336    * of the node.
337    *
338    * @param accessKey - Single character String or EL expression
339    * representing the label's access key.
340    */
341   public void setAccessKey (String accessKey)
342   {
343     if (   accessKey != null
344         && ContainerUtils.isValueReference(accessKey)
345        )
346     {
347        // EL Expression
348       _accessKey = accessKey;
349     }
350     else
351     {
352       // accessKey cannot be more than one character
353       if (accessKey != null && accessKey.length() > 1)
354         return;
355 
356       _accessKey = accessKey;
357     }
358   }
359 
360   /**
361    * setAccessKey - Takes a single character and sets the value of the
362    * accessKey attribute of the node.
363    *
364    * @param accessKey - Single character label access key.
365    */
366   public void setAccessKey (char accessKey)
367   {
368       char[] charArray = {'\0'};
369 
370       charArray[0] = accessKey;
371       _accessKey = String.copyValueOf(charArray);
372   }
373 
374   /**
375    * getAccessKey - get the label's accessKey as a char.
376    *
377    * @return the access key of the label as a char.
378    */
379   public char getAccessKey()
380   {
381     String accessKeyStr = MenuUtils.evalString(_accessKey);
382 
383     if (accessKeyStr == null || accessKeyStr.length() > 1)
384       return '\0';
385 
386     return accessKeyStr.charAt(0);
387   }
388 
389   /**
390    * setLabelAndAccessKey - Takes either an EL expression or a
391    * String representing the label and accessKey together, and
392    * sets the label and the accessKey separately.
393    *
394    * @param labelAndAccessKey - either and EL Expression or
395    * a String representing the label and accessKey together.
396    */
397   public void setLabelAndAccessKey(String labelAndAccessKey)
398   {
399     int ampIdx = 0;
400     _labelAndAccessKeyEL = false;
401 
402     // if EL expression, set it and the label to the same thing
403     if (   labelAndAccessKey != null
404         && ContainerUtils.isValueReference(labelAndAccessKey)
405        )
406     {
407       _labelAndAccessKey   = labelAndAccessKey;
408       _labelAndAccessKeyEL = true;
409       _accessKey = null;
410     }
411     else if (   labelAndAccessKey == null
412              || (ampIdx = labelAndAccessKey.indexOf('&')) == -1
413             )
414     {
415       // String is null or a label w/o an accesskey
416       _label = labelAndAccessKey;
417       _accessKey = null;
418     }
419     else if (ampIdx == (labelAndAccessKey.length() - 1))
420     {
421       // & is last character, strip it.
422       _label = labelAndAccessKey.substring(0, ampIdx);
423       _accessKey = null;
424     }
425     else
426     {
427        // We have a string with an accessKey somewhere
428        _splitLabelAndAccessKey(labelAndAccessKey);
429     }
430   }
431 
432   /**
433    * getLabelAndAccessKey - get the label and accessKey together
434    * in a single string.
435    *
436    * @return a String containing (representing) the label and accessKey
437    * together.
438    */
439   public String getLabelAndAccessKey()
440   {
441     // If labelAndAccessKey is an EL expression
442     // we get it and process it, set the label
443     // and the accessKey, null out labelAndAccessKey
444     // and set labelAndAccessKeyEL to false
445     if (_labelAndAccessKeyEL)
446     {
447       _labelAndAccessKey = _evalElStr(_labelAndAccessKey);
448       setLabelAndAccessKey(_labelAndAccessKey);
449       _labelAndAccessKey = null;
450       _labelAndAccessKeyEL = false;
451     }
452     // Now it is a simple string, so we have already
453     // set the label and accessKey.  We get both the
454     // label and accessKey, construct a labelAndAccessKey
455     // and return it.
456     String label = getLabel(); // This is a simple string
457 
458     if (_accessKey == null)
459       return label; // String is just the label
460 
461     return _joinLabelAndAccessKey(label, _accessKey);
462   }
463 
464   /**
465    * setId - sets the id of the node.
466    *
467    * @param id - the identifier for the node component
468    */
469   public void setId (String id)
470   {
471     _id = id;
472   }
473 
474   /**
475    * getId - gets the metadata id of the node.
476    *
477    * @return - String identifier for the node component.
478    */
479   public String getId()
480   {
481     return _id;
482   }
483 
484   /*===========================================================================
485    * getRefNode(), doAction(), & getDestination() are never called.  They
486    * are just here so that the same methods in itemNode.java and GroupNode.java
487    * will compile.
488    * ==========================================================================
489    */
490   /**
491    * Get the node whose id matches this node's
492    * idref attribute value.
493    *
494    * @return the MenuNode whose id matches this
495    *         node's idref attribute value.
496    */
497   public MenuNode getRefNode()
498   {
499     return this;
500   }
501 
502   /**
503    * Called by the Default ActionListener
504    * when a menu node is clicked/selected.
505    *
506    * @return String outcome or viewId used
507    *         during a POST for navigation.
508    */
509   public String doAction()
510   {
511     // Call the doAction method of my idref node
512     return getRefNode().doAction();
513   }
514 
515   /**
516    * Get the Destination URL of a page for a
517    * GET.
518    *
519    * @return String URL of a page.
520    */
521   public String getDestination()
522   {
523     // Call the doAction method of my idref node
524     return getRefNode().getDestination();
525   }
526 
527   /**
528    * Get the top-level, root menu model Request Map Key.
529    *
530    * @return root, top-level XMLMenuModel's Request Map Key.
531    */
532   public String getRootModelKey()
533   {
534     return _rootModelKey;
535   }
536 
537   /**
538    * Sets the root menu Model's Request map key.
539    * <p>
540    * This is always only the top-level, root model's Request map key.
541    * We do this because the MenuContentHandlerImpl and nodes need to be able
542    * to call into the root model to:
543    * <ul>
544    * <li>notify them root menu model of the currently selected node on a POST
545    * <li>group node needs to find its referenced item node.
546    * </ul>
547    *
548    * @param rootModelKey - String the root, top-level menu model's Request
549    *        map key.
550    */
551   public void setRootModelKey(String rootModelKey)
552   {
553     _rootModelKey = rootModelKey;
554   }
555 
556   /**
557    * Gets the local (shared node's) menu Model's Request map key.
558    */
559   public String getModelId()
560   {
561     return _modelId;
562   }
563 
564   /**
565    * Sets the local (shared node's) menu Model's Request map key.
566    * <p>
567    * This is appended to the node's id to create a unique id.
568    *
569    * @param rootModelKey - String the local (shared node's) menu
570    *        Model's Request map key.
571    */
572   public void setModelId(String modelId)
573   {
574     _modelId = modelId;
575   }
576 
577   public int getRootId()
578   {
579     return _rootId;
580   }
581   public void setRootId(int id)
582   {
583     _rootId = id;
584   }
585 
586   
587   public final String getLabelProperty()
588   {
589     return _label;
590   }
591 
592   public final String getIconProperty()
593   {
594     return _icon;
595   }
596 
597   public final List<MenuNode> getChildrenProperty()
598   {
599     return _children;
600   }
601 
602   public final String getFocusViewIdProperty()
603   {
604     return _focusViewId;
605   }
606 
607   public final String getRenderedProperty()
608   {
609     return _renderedStr;
610   }
611 
612   public final String getDisabledProperty()
613   {
614     return _disabledStr;
615   }
616 
617   public final String getVisibleProperty()
618   {
619     return _visibleStr;
620   }
621 
622   public final String getReadOnlyProperty()
623   {
624     return _readOnlyStr;
625   }
626 
627   public final String getHandlerIdProperty()
628   {
629     return _handlerId;
630   }
631 
632   public final String getBundleKeyProperty()
633   {
634     return _bundleKey;
635   }
636 
637   public final String getBundleNameProperty()
638   {
639     return _bundleName;
640   }
641 
642   public final String getAccessKeyProperty()
643   {
644     return _accessKey;
645   }
646 
647   public final String getIdProperty()
648   {
649     return _id;
650   }
651 
652   public final String getModelIdProperty()
653   {
654     return _modelId;
655   }
656 
657   public final String getUniqueIdProperty()
658   {
659     return getIdProperty() + getModelIdProperty();
660   }
661 
662   
663   public final String getLabelAndAccessKeyProperty()
664   {
665     return _labelAndAccessKey;
666   }
667 
668   public final String getDefaultFocusPathProperty()
669   {
670     return _defaultFocusPathStr;
671   }
672 
673   public final String getRootModelKeyProperty()
674   {
675     return _rootModelKey;
676   }
677 
678   public final int getRootIdProperty()
679   {
680     return _rootId;
681   }
682   
683   /**
684    * setResBundleKey - sets the name of the resource bundle used in
685    * obtaining the node's label text. Used, along with the handerId,
686    * to identify and get a string from the proper resource bundle.
687    *
688    * @param bundleKey - String name of the resource bundle.
689    */
690   public void setResBundleKey(String bundleKey)
691   {
692     _bundleKey = bundleKey;
693   }
694 
695   /**
696    * setResBundleKey - sets the name of the resource bundle used in
697    * obtaining the node's label text. Used, along with the handerId,
698    * to identify and get a string from the proper resource bundle.
699    *
700    * @param bundleName - String name of the resource bundle.
701    */
702   public void setResBundleName(String bundleName)
703   {
704     _bundleName = bundleName;
705   }
706 
707   /**
708    * setHandlerId - sets the MenuContentHandlerImpl's handlerId on the node.
709    * Used, along with the bundleKey, to identify and get a string from the
710    * proper resource bundle.
711    *
712    * @param handlerId String uniquely identifying the specific
713    *        MenuContentHandlerImpl that created this node.
714    */
715   public void setHandlerId(String handlerId)
716   {
717     _handlerId = handlerId;
718   }
719 
720   /**
721    * Notifies the root model that this node has been selected on a POST
722    *
723    * @param selectedNode - The currently selected menu item.
724    */
725   protected void postSelectedNode(MenuNode selectedNode)
726   {
727     getRootModel().setCurrentlyPostedNode(selectedNode);
728   }
729 
730   /**
731    * getUniqueId - gets the unique id of the node.
732    *
733    * @return - String identifier for the node component.
734    */
735   public String getUniqueId()
736   {
737     // This must be made unique so that we do not have duplicates
738     // in the idNodeMap on the menu's tree.
739     return _id + _modelId;
740   }
741 
742   /**
743    * Set the MenuContentHandlerImpl's id.
744    *
745    * @return String object id of the MenuContentHandlerImpl
746    */
747   protected String getHandlerId()
748   {
749     return _handlerId;
750   }
751 
752   /**
753    * Get the top-level, root menu model, which contains
754    * the entire menu tree.
755    *
756    * @return root, top-level XMLMenuModel
757    */
758   @SuppressWarnings("unchecked")
759   protected XMLMenuModel getRootModel()
760   {
761     FacesContext facesContext = FacesContext.getCurrentInstance();
762     Map<String, Object> requestMap =
763       facesContext.getExternalContext().getRequestMap();
764 
765     Map map =  (Map) requestMap.get(getRootModelKey());
766     XMLMenuModel model = (XMLMenuModel) map.get(getRootId());
767     return model;
768   }
769 
770   /**
771    * Construct a thread safe version
772    * of this object and return it.
773    * @return a thread safe copy of this object. 
774    */
775   public MenuNode getThreadSafeCopy()
776   {
777     return null;
778   }
779   /**
780    * _joinLabelAndAccessKey - takes a string label and string accessKey
781    * and combines them into a single labelAndAccessKey string.
782    *
783    * @param label - String with node's label.
784    * @param accessKey - One character String which is the label's accessKey.
785    * @return
786    */
787   private String _joinLabelAndAccessKey(String label, String accessKey)
788   {
789     char[] keyArray  = label.toCharArray();
790     int len          = label.length();
791     int lentimes2    = len*2;
792     char[] keyArray2 = new char[lentimes2];
793     int i, j = 0;
794     boolean accessKeyFound = false;
795 
796     // find the first occurrence of a single Ampersand
797     for (i=0, j=0; i < len; i++, j++)
798     {
799       // AccessKey
800       if (   keyArray[i] == accessKey.charAt(0)
801           && !accessKeyFound
802          )
803       {
804         keyArray2[j] = '&';
805         j++;
806         accessKeyFound = true;
807       }
808 
809       keyArray2[j] = keyArray[i];
810 
811       // Ampersand as regular character
812       // double it up.
813       if (keyArray[i] == '&')
814       {
815         j++;
816         keyArray2[j] = keyArray[i];
817       }
818     }
819 
820     String combinedLabel = new String(keyArray2, 0, j);
821     return combinedLabel;
822   }
823 
824   /**
825    * _splitLabelAndAccessKey - takes a string containing a label
826    * and an accessKey and breaks separates it and sets the label
827    * and the accessKey separately.
828    *
829    * @param labelAndAccessKey - String holding both a label and
830    * accessKey.
831    */
832   private void _splitLabelAndAccessKey(String labelAndAccessKey)
833   {
834     char[] keyArray  = labelAndAccessKey.toCharArray();
835     int len = labelAndAccessKey.length();
836     char[] keyArray2 = new char[len];
837     int i, j = 0;
838     boolean accessKeyFound = false;
839 
840     for (i=0, j=0; i < len ; i++, j++)
841     {
842       if (keyArray[i] == '&')
843       {
844          i++;
845 
846          if (!accessKeyFound && keyArray[i] != '&')
847          {
848            // We have our accessKey
849            _accessKey = labelAndAccessKey.substring(i, i+1);
850            accessKeyFound = true;
851          }
852       }
853 
854       keyArray2[j] = keyArray[i];
855     }
856 
857     String label = new String(keyArray2, 0, j);
858     _label = label;
859   }
860 
861   /**
862    * _evalElStr - Evaluate an EL expression string.
863    *
864    * @param str - the EL expression
865    * @return the bound value of the El expression as a String
866    */
867   private String _evalElStr(String str)
868   {
869     if (str == null)
870       return null;
871 
872     String keystr = MenuUtils.stringReplaceFirst(str.trim(), _bundleKey,
873                                                  _bundleKey + getHandlerId());
874     String elVal = MenuUtils.getBoundValue(keystr, String.class);
875     return elVal;
876   }
877 
878   private String         _label       = null;
879   private String         _icon        = null;
880   private List<MenuNode> _children    = null;
881   private String         _focusViewId = null;
882   private String         _renderedStr = null;
883   private String         _disabledStr = null;
884   private String         _visibleStr  = null;
885   private String         _readOnlyStr = null;
886   private String         _handlerId   = null;
887   private String         _bundleKey   = null;
888   private String         _bundleName  = null;
889   private String         _accessKey   = null;
890   private String         _id          = null;
891   private String         _modelId     = null;
892   private boolean        _labelAndAccessKeyEL = false;
893   private String         _labelAndAccessKey   = null;
894   private String         _defaultFocusPathStr = null;
895 
896   // Root Menu model's Request Map Key
897   private String _rootModelKey  = null;
898   
899   private int _rootId;
900 
901 }