View Javadoc

1   /*
2    * $Id: ComponentTools.java,v 1.18 2011/06/16 09:29:41 jbmeslin Exp $
3    * 
4    */
5   package org.rcfaces.core.internal.tools;
6   
7   import java.io.IOException;
8   import java.lang.reflect.Array;
9   import java.util.ArrayList;
10  import java.util.Iterator;
11  import java.util.List;
12  import java.util.Map;
13  import java.util.StringTokenizer;
14  
15  import javax.faces.application.FacesMessage;
16  import javax.faces.component.NamingContainer;
17  import javax.faces.component.UICommand;
18  import javax.faces.component.UIComponent;
19  import javax.faces.component.UIInput;
20  import javax.faces.component.UIViewRoot;
21  import javax.faces.context.FacesContext;
22  import javax.faces.convert.Converter;
23  import javax.faces.convert.ConverterException;
24  import javax.faces.event.ActionEvent;
25  import javax.faces.event.ActionListener;
26  import javax.faces.event.FacesEvent;
27  import javax.faces.event.FacesListener;
28  import javax.faces.event.PhaseId;
29  
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogFactory;
32  import org.rcfaces.core.component.capability.IAsyncDecodeModeCapability;
33  import org.rcfaces.core.component.capability.IPrependIdCapability;
34  import org.rcfaces.core.internal.RcfacesContext;
35  import org.rcfaces.core.internal.adapter.IAdapterManager;
36  import org.rcfaces.core.internal.capability.IComponentEngine;
37  import org.rcfaces.core.internal.lang.StringAppender;
38  import org.rcfaces.core.internal.listener.IScriptListener;
39  import org.rcfaces.core.internal.renderkit.WriterException;
40  import org.rcfaces.core.internal.util.ComponentIterators;
41  import org.rcfaces.core.lang.IAdaptable;
42  import org.rcfaces.core.lang.provider.ICursorProvider;
43  
44  /**
45   * 
46   * @author Olivier Oeuillot (latest modification by $Author: jbmeslin $)
47   * @version $Revision: 1.18 $ $Date: 2011/06/16 09:29:41 $
48   */
49  public final class ComponentTools extends ComponentTools0 {
50      private static final String REVISION = "$Revision: 1.18 $";
51  
52      private static final Log LOG = LogFactory.getLog(ComponentTools.class);
53  
54      public static final String[] STRING_EMPTY_ARRAY = new String[0];
55  
56      static final String VARIABLE_SCOPE_VALUE = "org.rcfaces.core.internal.VARIABLE_SCOPE_VALUE";
57  
58      static final String NONE_VARIABLE_SCOPE = "##~NONE~"
59              + System.currentTimeMillis();
60  
61      private static final UIComponent[] COMPONENT_EMPTY_ARRAY = new UIComponent[0];
62  
63      public static final String PARTIAL_ASYNC_DECODE_PROPERTY = "org.rcfaces.async.partial";
64  
65      public static final boolean isAnonymousComponentId(String componentId) {
66          if (componentId == null) {
67              return false;
68          }
69  
70          int idx = componentId.lastIndexOf(NamingContainer.SEPARATOR_CHAR);
71          if (idx >= 0) {
72              componentId = componentId.substring(idx + 1);
73          }
74  
75          return componentId.startsWith(UIViewRoot.UNIQUE_ID_PREFIX);
76      }
77  
78      public static final void encodeRecursive(FacesContext context,
79              UIComponent component) throws WriterException {
80          if (component.isRendered() == false) {
81              return;
82          }
83  
84          try {
85              component.encodeBegin(context);
86  
87              if (component.getRendersChildren()) {
88                  component.encodeChildren(context);
89  
90              } else {
91                  encodeChildrenRecursive(context, component);
92              }
93  
94              component.encodeEnd(context);
95  
96          } catch (WriterException ex) {
97              throw ex;
98  
99          } catch (IOException ex) {
100             throw new WriterException("Can not encode component '"
101                     + component.getId() + "'.", ex, component);
102         }
103     }
104 
105     public static final void encodeChildrenRecursive(FacesContext context,
106             UIComponent component) throws WriterException {
107         Iterator children = component.getChildren().iterator();
108 
109         for (; children.hasNext();) {
110             UIComponent child = (UIComponent) children.next();
111             if (child.isRendered() == false) {
112                 continue;
113             }
114 
115             try {
116                 child.encodeBegin(context);
117 
118                 if (child.getRendersChildren()) {
119                     child.encodeChildren(context);
120 
121                 } else {
122                     encodeChildrenRecursive(context, child);
123                 }
124 
125                 child.encodeEnd(context);
126 
127             } catch (WriterException ex) {
128                 throw ex;
129 
130             } catch (IOException ex) {
131                 throw new WriterException("Can not encode child component '"
132                         + child.getId() + "'.", ex, child);
133             }
134         }
135     }
136 
137     public static UIComponent getForComponent(FacesContext context,
138             String forComponent, UIComponent component) {
139         if (forComponent == null || forComponent.length() < 1) {
140             return null;
141         }
142 
143         try {
144             // Check the naming container of the current
145             // component for component identified by
146             // 'forComponent'
147             for (UIComponent currentParent = component; currentParent != null; currentParent = currentParent
148                     .getParent()) {
149 
150                 try {
151                     UIComponent result = currentParent
152                             .findComponent(forComponent);
153                     if (result != null) {
154                         if (LOG.isDebugEnabled()) {
155                             LOG.debug("Get for component '" + forComponent
156                                     + "' returns " + result.getId());
157                         }
158                         return result;
159                     }
160 
161                 } catch (IllegalArgumentException ex) {
162                     LOG.error("Compute for component '" + component.getId()
163                             + "'", ex);
164 
165                     break;
166                 }
167             }
168 
169             return findUIComponentBelow(context.getViewRoot(), forComponent);
170 
171         } catch (RuntimeException ex) {
172             LOG.error("Compute for component '" + component.getId() + "'", ex);
173 
174             throw ex;
175         }
176     }
177 
178     private static UIComponent findUIComponentBelow(UIComponent startPoint,
179             String forComponent) {
180         List children = startPoint.getChildren();
181 
182         if (LOG.isDebugEnabled()) {
183             LOG.debug("Find component below '" + startPoint.getId() + "' to '"
184                     + forComponent + "'.");
185         }
186 
187         int size = children.size();
188         for (int i = 0; i < size; i++) {
189             UIComponent comp = (UIComponent) children.get(i);
190 
191             boolean nc = (comp instanceof NamingContainer);
192             if (nc) {
193                 if (comp instanceof IPrependIdCapability) {
194                     nc = ((IPrependIdCapability) comp).isPrependId();
195                 }
196             }
197 
198             if (nc) {
199                 try {
200                     UIComponent retComp = comp.findComponent(forComponent);
201 
202                     if (retComp != null) {
203                         if (LOG.isDebugEnabled()) {
204                             LOG.debug("Find component below '" + forComponent
205                                     + "' returns " + retComp.getId());
206                         }
207 
208                         return retComp;
209                     }
210 
211                 } catch (IllegalArgumentException ex) {
212                     LOG.error("Find component below '" + startPoint.getId()
213                             + "' to '" + forComponent + "' exception:", ex);
214                 }
215             }
216 
217             if (comp.getChildCount() > 0) {
218                 UIComponent retComp = findUIComponentBelow(comp, forComponent);
219                 if (retComp != null) {
220                     return retComp;
221                 }
222             }
223         }
224 
225         return null;
226     }
227 
228     public static UIViewRoot getViewRoot(UIComponent component) {
229         for (; component != null;) {
230 
231             if (component instanceof UIViewRoot) {
232                 return (UIViewRoot) component;
233             }
234 
235             component = component.getParent();
236         }
237 
238         return null;
239     }
240 
241     public static Converter createConverter(FacesContext facesContext,
242             String converterId) {
243         if (facesContext == null) {
244             facesContext = FacesContext.getCurrentInstance();
245         }
246 
247         Converter converter = facesContext.getApplication().createConverter(
248                 converterId);
249 
250         if (LOG.isDebugEnabled()) {
251             LOG.debug("Get converter id=" + converterId + "' object="
252                     + converter);
253         }
254 
255         return converter;
256     }
257 
258     /**
259      * 
260      * @author Olivier Oeuillot (latest modification by $Author: jbmeslin $)
261      * @version $Revision: 1.18 $ $Date: 2011/06/16 09:29:41 $
262      */
263     private static class Entry implements Map.Entry {
264         private final Object key;
265 
266         private Object value;
267 
268         /**
269          * Create new entry.
270          */
271         Entry(Object k, Object v) {
272             value = v;
273             key = k;
274         }
275 
276         public Object getKey() {
277             return key;
278         }
279 
280         public Object getValue() {
281             return value;
282         }
283 
284         public boolean equals(Object o) {
285             if (!(o instanceof Map.Entry)) {
286                 return false;
287             }
288 
289             Map.Entry e = (Map.Entry) o;
290             Object k1 = getKey();
291             Object k2 = e.getKey();
292             if (k1 == k2 || (k1 != null && k1.equals(k2))) {
293                 Object v1 = getValue();
294                 Object v2 = e.getValue();
295                 if (v1 == v2 || (v1 != null && v1.equals(v2))) {
296                     return true;
297                 }
298             }
299             return false;
300         }
301 
302         public int hashCode() {
303             return key.hashCode() ^ (value == null ? 0 : value.hashCode());
304         }
305 
306         public String toString() {
307             return getKey() + "=" + getValue();
308         }
309 
310         public Object setValue(Object value) {
311             throw new UnsupportedOperationException("Not implemented !");
312         }
313     }
314 
315     public static UIComponent findComponent(UIComponent startPoint, Class clazz) {
316         UIComponent retComp = null;
317         List children = startPoint.getChildren();
318 
319         int size = children.size();
320         for (int i = 0; i < size; i++) {
321             UIComponent comp = (UIComponent) children.get(i);
322 
323             if (clazz.isInstance(comp)) {
324                 return comp;
325             }
326 
327             if (comp.getChildCount() > 0) {
328                 retComp = findComponent(comp, clazz);
329                 if (retComp != null) {
330                     return retComp;
331                 }
332             }
333         }
334 
335         return null;
336     }
337 
338     /**
339      * 
340      * @author Olivier Oeuillot (latest modification by $Author: jbmeslin $)
341      * @version $Revision: 1.18 $ $Date: 2011/06/16 09:29:41 $
342      */
343     public interface IVarScope {
344         void popVar(FacesContext facesContext);
345     }
346 
347     /**
348      * 
349      * @author Olivier Oeuillot (latest modification by $Author: jbmeslin $)
350      * @version $Revision: 1.18 $ $Date: 2011/06/16 09:29:41 $
351      */
352     static class VarScope implements IVarScope {
353         private static final String REVISION = "$Revision: 1.18 $";
354 
355         private final String var;
356 
357         private final Object previousObject;
358 
359         public VarScope(String var, Object previousObject) {
360             this.var = var;
361             this.previousObject = previousObject;
362         }
363 
364         public void popVar(FacesContext facesContext) {
365             Map requestMap = facesContext.getExternalContext().getRequestMap();
366 
367             if (previousObject == null) {
368                 requestMap.remove(var);
369                 return;
370             }
371 
372             requestMap.put(var, previousObject);
373         }
374     }
375 
376     public static void broadcast(UIComponent component, FacesEvent facesEvent,
377             FacesListener listeners[]) {
378 
379         if (facesEvent == null) {
380             throw new NullPointerException("Event is null");
381         }
382 
383         if (listeners == null || listeners.length < 1) {
384             return;
385         }
386 
387         for (int i = 0; i < listeners.length; i++) {
388             FacesListener listener = listeners[i];
389 
390             if (facesEvent.isAppropriateListener(listener) == false) {
391                 continue;
392             }
393 
394             if (listener instanceof IScriptListener) {
395                 continue;
396             }
397 
398             facesEvent.processListener(listener);
399         }
400     }
401 
402     public static Object getCursorValue(Object value, UIComponent component,
403             FacesContext facesContext) {
404         if (value == null) {
405             return null;
406         }
407 
408         ICursorProvider cursorProvider = null;
409 
410         if (value instanceof IAdaptable) {
411             cursorProvider = (ICursorProvider) ((IAdaptable) value).getAdapter(
412                     ICursorProvider.class, component);
413 
414             if (LOG.isDebugEnabled()) {
415                 LOG.debug("Try to adapt '" + value + "' to ICursorProvider => "
416                         + cursorProvider);
417             }
418         }
419 
420         if (cursorProvider == null) {
421             IAdapterManager adapterManager = RcfacesContext.getInstance(
422                     facesContext).getAdapterManager();
423 
424             cursorProvider = (ICursorProvider) adapterManager.getAdapter(value,
425                     ICursorProvider.class, component);
426 
427             if (LOG.isDebugEnabled()) {
428                 LOG.debug("Ask adapterManager to adapt '" + value
429                         + "' to ICursorProvider => " + cursorProvider);
430             }
431         }
432 
433         if (cursorProvider == null) {
434             return null;
435         }
436 
437         return cursorProvider.getCursorValue();
438     }
439 
440     public static void addConversionErrorMessage(FacesContext context,
441             UIInput input, ConverterException ce, Object submittedValue) {
442         FacesMessage message = ce.getFacesMessage();
443         if (message == null) {
444             message = new FacesMessage("Conversion error !"); // TODO
445             // MessageFactory.getMessage(context,
446             // CONVERSION_MESSAGE_ID);
447             if (message.getDetail() == null) {
448                 message.setDetail(ce.getMessage());
449             }
450         }
451 
452         message.setSeverity(FacesMessage.SEVERITY_ERROR);
453         context.addMessage(input.getClientId(context), message);
454 
455     }
456 
457     public static void broadcastCommand(UICommand component,
458             ActionEvent facesEvent, FacesListener listeners[]) {
459 
460         if (facesEvent == null) {
461             throw new NullPointerException("Event is null");
462         }
463 
464         boolean found = false;
465 
466         FacesContext facesContext = FacesContext.getCurrentInstance();
467 
468         if (listeners != null && listeners.length > 0) {
469             for (int i = 0; i < listeners.length; i++) {
470                 FacesListener listener = listeners[i];
471 
472                 if (listener instanceof IScriptListener) {
473                     continue;
474                 }
475 
476                 if (facesEvent.isAppropriateListener(listener) == false) {
477                     continue;
478                 }
479 
480                 facesEvent.processListener(listener);
481 
482                 found = true;
483 
484                 /*
485                  * On les appelle tous ? if (facesContext.getRenderResponse()) {
486                  * break; }
487                  */
488             }
489         }
490 
491         if (found == false) {
492             BindingTools.invokeActionListener(facesContext, component,
493                     facesEvent);
494 
495             // Invoke the default ActionListener
496             ActionListener listener = facesContext.getApplication()
497                     .getActionListener();
498             if (listener != null) {
499                 listener.processAction(facesEvent);
500             }
501         }
502 
503         if (facesContext.getRenderResponse()) {
504             return;
505         }
506 
507         broadcastActionCommand(facesContext, component);
508     }
509 
510     public static UIComponent[] listChildren(FacesContext facesContext,
511             UIComponent component, IComponentEngine engine, Class childClass,
512             String propertyName) {
513 
514         String ids = engine.getStringProperty(propertyName, facesContext);
515 
516         return listChildren(facesContext, component, ids, childClass);
517     }
518 
519     public static UIComponent[] listChildren(FacesContext facesContext,
520             UIComponent component, String ids, Class childClass) {
521 
522         if (ids == null) {
523             if (childClass == null || childClass == UIComponent.class) {
524                 return COMPONENT_EMPTY_ARRAY;
525             }
526             return (UIComponent[]) Array.newInstance(childClass, 0);
527         }
528 
529         StringTokenizer st = new StringTokenizer(ids, ",");
530 
531         if (st.hasMoreTokens() == false) {
532             if (childClass == null || childClass == UIComponent.class) {
533                 return COMPONENT_EMPTY_ARRAY;
534             }
535             return (UIComponent[]) Array.newInstance(childClass, 0);
536         }
537 
538         Object children[] = ComponentIterators.list(component, childClass)
539                 .toArray();
540 
541         List l = new ArrayList(st.countTokens());
542 
543         for (; st.hasMoreTokens();) {
544             String id = st.nextToken().trim();
545 
546             if (id.startsWith("#")) {
547                 int idx = Integer.parseInt(id.substring(1));
548 
549                 if (idx >= children.length) {
550                     continue;
551                 }
552 
553                 UIComponent child = (UIComponent) children[idx];
554 
555                 l.add(child);
556                 continue;
557             }
558 
559             for (int i = 0; i < children.length; i++) {
560                 UIComponent child = (UIComponent) children[i];
561 
562                 if (id.equals(child.getId()) == false) {
563                     continue;
564                 }
565 
566                 l.add(child);
567                 break;
568             }
569         }
570 
571         if (childClass == null) {
572             childClass = UIComponent.class;
573         }
574 
575         return (UIComponent[]) l.toArray((UIComponent[]) Array.newInstance(
576                 childClass, l.size()));
577     }
578 
579     public static void setChildren(UIComponent component,
580             IComponentEngine engine, Class classOfChild,
581             UIComponent[] children, String propertyName) {
582 
583         String list = createStringFromList(children, classOfChild, component);
584 
585         engine.setProperty(propertyName, list);
586     }
587 
588     private static String createStringFromList(UIComponent[] components,
589             Class classOfChild, UIComponent component) {
590         if (components == null) {
591             return null;
592         }
593 
594         if (components.length < 1) {
595             return "";
596         }
597 
598         Object children[] = null;
599 
600         StringAppender sa = new StringAppender(components.length * 32);
601         for (int i = 0; i < components.length; i++) {
602             UIComponent child = components[i];
603 
604             if (classOfChild.isInstance(child) == false) {
605                 continue;
606             }
607 
608             if (sa.length() > 0) {
609                 sa.append(',');
610             }
611 
612             String id = child.getId();
613 
614             if (id.startsWith(UIViewRoot.UNIQUE_ID_PREFIX)) {
615                 int childIndex = -1;
616 
617                 if (children == null) {
618                     children = ComponentIterators.list(component, classOfChild)
619                             .toArray();
620                 }
621 
622                 for (int j = 0; j < children.length; j++) {
623                     if (children[j] != child) {
624                         continue;
625                     }
626 
627                     childIndex = j;
628                     break;
629                 }
630                 if (childIndex < 0) {
631                     LOG.error("Can not find index of component '" + child
632                             + "'.");
633                     continue;
634                 }
635 
636                 sa.append('#').append(childIndex);
637                 continue;
638             }
639 
640             sa.append(child.getId());
641         }
642 
643         return sa.toString();
644     }
645 
646     public static boolean hasValidationServerListeners(
647             FacesListener facesListeners[]) {
648         for (int i = 0; i < facesListeners.length; i++) {
649             FacesListener facesListener = facesListeners[i];
650 
651             if (facesListener instanceof IScriptListener) {
652                 continue;
653             }
654 
655             return true;
656         }
657 
658         return false;
659     }
660 
661     public static boolean verifyAsyncDecode(FacesContext facesContext,
662             IAsyncDecodeModeCapability asyncDecodeModeCapability,
663             PhaseId phaseId) {
664 
665         if (asyncDecodeModeCapability.getAsyncDecodeMode() != IAsyncDecodeModeCapability.PARTIAL_ASYNC_DECODE_MODE) {
666             return true;
667         }
668 
669         Map parameters = facesContext.getExternalContext()
670                 .getRequestParameterMap();
671 
672         UIComponent component = (UIComponent) asyncDecodeModeCapability;
673 
674         String clientId = component.getClientId(facesContext);
675 
676         String value = (String) parameters.get(PARTIAL_ASYNC_DECODE_PROPERTY
677                 + "." + clientId);
678 
679         if (value != null) {
680             if (LOG.isDebugEnabled()) {
681                 LOG.debug("Partial async decode enabled for component '"
682                         + clientId + "' phase='" + phaseId + "'.");
683             }
684 
685             return true;
686         }
687 
688         if (LOG.isDebugEnabled()) {
689             LOG.debug("Partial async decode DISABLED for component '"
690                     + clientId + "' phase='" + phaseId + "'.");
691         }
692         return false;
693     }
694 }