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.view.facelets.compiler;
20  
21  import java.io.IOException;
22  import java.util.Collection;
23  import java.util.Collections;
24  import java.util.HashMap;
25  import java.util.Iterator;
26  import java.util.List;
27  import java.util.Map;
28  
29  import java.util.Set;
30  import javax.el.ValueExpression;
31  import javax.faces.FacesException;
32  import javax.faces.component.ContextCallback;
33  import javax.faces.component.UIComponent;
34  import javax.faces.component.UIViewRoot;
35  import javax.faces.component.UniqueIdVendor;
36  import javax.faces.component.visit.VisitCallback;
37  import javax.faces.component.visit.VisitContext;
38  import javax.faces.context.FacesContext;
39  import javax.faces.el.ValueBinding;
40  import javax.faces.event.AbortProcessingException;
41  import javax.faces.event.ComponentSystemEvent;
42  import javax.faces.event.FacesEvent;
43  import javax.faces.event.FacesListener;
44  import javax.faces.render.Renderer;
45  
46  import javax.faces.view.Location;
47  import org.apache.commons.collections.iterators.EmptyIterator;
48  import org.apache.myfaces.view.facelets.tag.jsf.ComponentSupport;
49  
50  class UILeaf extends UIComponent implements Map<String, Object>
51  {
52      //-------------- START TAKEN FROM UIComponentBase ----------------
53      private static final String _STRING_BUILDER_KEY
54              = "javax.faces.component.UIComponentBase.SHARED_STRING_BUILDER";
55      
56      private String _clientId = null;
57      
58      private String _id = null;
59  
60      public String getClientId(FacesContext context)
61      {
62          if (context == null)
63          {
64              throw new NullPointerException("context");
65          }
66  
67          if (_clientId != null)
68          {
69              return _clientId;
70          }
71  
72          //boolean idWasNull = false;
73          String id = getId();
74          if (id == null)
75          {
76              // Although this is an error prone side effect, we automatically create a new id
77              // just to be compatible to the RI
78              
79              // The documentation of UniqueIdVendor says that this interface should be implemented by
80              // components that also implements NamingContainer. The only component that does not implement
81              // NamingContainer but UniqueIdVendor is UIViewRoot. Anyway we just can't be 100% sure about this
82              // fact, so it is better to scan for the closest UniqueIdVendor. If it is not found use 
83              // viewRoot.createUniqueId, otherwise use UniqueIdVendor.createUniqueId(context,seed).
84              UniqueIdVendor parentUniqueIdVendor = _ComponentUtils.findParentUniqueIdVendor(this);
85              if (parentUniqueIdVendor == null)
86              {
87                  UIViewRoot viewRoot = context.getViewRoot();
88                  if (viewRoot != null)
89                  {
90                      id = viewRoot.createUniqueId();
91                  }
92                  else
93                  {
94                      // The RI throws a NPE
95                      String location = getComponentLocation(this);
96                      throw new FacesException("Cannot create clientId. No id is assigned for component"
97                              + " to create an id and UIViewRoot is not defined: "
98                              + getPathToComponent(this)
99                              + (location != null ? " created from: " + location : ""));
100                 }
101             }
102             else
103             {
104                 id = parentUniqueIdVendor.createUniqueId(context, null);
105             }
106             setId(id);
107             // We remember that the id was null and log a warning down below
108             // idWasNull = true;
109         }
110 
111         UIComponent namingContainer = _ComponentUtils.findParentNamingContainer(this, false);
112         if (namingContainer != null)
113         {
114             String containerClientId = namingContainer.getContainerClientId(context);
115             if (containerClientId != null)
116             {
117                 StringBuilder bld = _getSharedStringBuilder(context);
118                 _clientId = bld.append(containerClientId).append(
119                                       context.getNamingContainerSeparatorChar()).append(id).toString();
120             }
121             else
122             {
123                 _clientId = id;
124             }
125         }
126         else
127         {
128             _clientId = id;
129         }
130 
131         Renderer renderer = getRenderer(context);
132         if (renderer != null)
133         {
134             _clientId = renderer.convertClientId(context, _clientId);
135         }
136 
137         // -=Leonardo Uribe=- In jsf 1.1 and 1.2 this warning has sense, but in jsf 2.0 it is common to have
138         // components without any explicit id (UIViewParameter components and UIOuput resource components) instances.
139         // So, this warning is becoming obsolete in this new context and should be removed.
140         //if (idWasNull && log.isLoggable(Level.WARNING))
141         //{
142         //    log.warning("WARNING: Component " + _clientId
143         //            + " just got an automatic id, because there was no id assigned yet. "
144         //            + "If this component was created dynamically (i.e. not by a JSP tag) you should assign it an "
145         //            + "explicit static id or assign it the id you get from "
146         //            + "the createUniqueId from the current UIViewRoot "
147         //            + "component right after creation! Path to Component: " + getPathToComponent(this));
148         //}
149 
150         return _clientId;
151     }
152     
153     public String getId()
154     {
155         return _id;
156     }
157     
158     @Override
159     public void setId(String id)
160     {
161         // UILeaf instance are just a wrapper for html markup. It never has 
162         // an user defined id. The validation check here is just useless, 
163         // because facelets algorithm ensures that.
164         //isIdValid(id);
165         _id = id;
166         _clientId = null;
167     }
168     /*
169     private void isIdValid(String string)
170     {
171 
172         // is there any component identifier ?
173         if (string == null)
174         {
175             return;
176         }
177 
178         // Component identifiers must obey the following syntax restrictions:
179         // 1. Must not be a zero-length String.
180         if (string.length() == 0)
181         {
182             throw new IllegalArgumentException("component identifier must not be a zero-length String");
183         }
184 
185         // If new id is the same as old it must be valid
186         if (string.equals(_id))
187         {
188             return;
189         }
190 
191         // 2. First character must be a letter or an underscore ('_').
192         if (!Character.isLetter(string.charAt(0)) && string.charAt(0) != '_')
193         {
194             throw new IllegalArgumentException("component identifier's first character must be a letter "
195                                                + "or an underscore ('_')! But it is \""
196                                                + string.charAt(0) + "\"");
197         }
198         for (int i = 1; i < string.length(); i++)
199         {
200             char c = string.charAt(i);
201             // 3. Subsequent characters must be a letter, a digit, an underscore ('_'), or a dash ('-').
202             if (!Character.isLetterOrDigit(c) && c != '-' && c != '_')
203             {
204                 throw new IllegalArgumentException("Subsequent characters of component identifier must be a letter, "
205                                                    + "a digit, an underscore ('_'), or a dash ('-')! "
206                                                    + "But component identifier contains \""
207                                                    + c + "\"");
208             }
209         }
210     }*/
211     
212     private String getComponentLocation(UIComponent component)
213     {
214         Location location = (Location) component.getAttributes()
215                 .get(UIComponent.VIEW_LOCATION_KEY);
216         if (location != null)
217         {
218             return location.toString();
219         }
220         return null;
221     }
222 
223     private String getPathToComponent(UIComponent component)
224     {
225         StringBuffer buf = new StringBuffer();
226 
227         if (component == null)
228         {
229             buf.append("{Component-Path : ");
230             buf.append("[null]}");
231             return buf.toString();
232         }
233 
234         getPathToComponent(component, buf);
235 
236         buf.insert(0, "{Component-Path : ");
237         buf.append("}");
238 
239         return buf.toString();
240     }
241 
242     private void getPathToComponent(UIComponent component, StringBuffer buf)
243     {
244         if (component == null)
245         {
246             return;
247         }
248 
249         StringBuffer intBuf = new StringBuffer();
250 
251         intBuf.append("[Class: ");
252         intBuf.append(component.getClass().getName());
253         if (component instanceof UIViewRoot)
254         {
255             intBuf.append(",ViewId: ");
256             intBuf.append(((UIViewRoot) component).getViewId());
257         }
258         else
259         {
260             intBuf.append(",Id: ");
261             intBuf.append(component.getId());
262         }
263         intBuf.append("]");
264 
265         buf.insert(0, intBuf.toString());
266 
267         getPathToComponent(component.getParent(), buf);
268     }
269     
270     static StringBuilder _getSharedStringBuilder(FacesContext facesContext)
271     {
272         Map<Object, Object> attributes = facesContext.getAttributes();
273 
274         StringBuilder sb = (StringBuilder) attributes.get(_STRING_BUILDER_KEY);
275 
276         if (sb == null)
277         {
278             sb = new StringBuilder();
279             attributes.put(_STRING_BUILDER_KEY, sb);
280         }
281         else
282         {
283 
284             // clear out the stringBuilder by setting the length to 0
285             sb.setLength(0);
286         }
287 
288         return sb;
289     }
290 
291     //-------------- END TAKEN FROM UICOMPONENTBASE ------------------
292     
293 
294     private static Map<String, UIComponent> facets = new HashMap<String, UIComponent>()
295     {
296 
297         @Override
298         public void putAll(Map<? extends String, ? extends UIComponent> map)
299         {
300             // do nothing
301         }
302 
303         @Override
304         public UIComponent put(String name, UIComponent value)
305         {
306             return null;
307         }
308     };
309 
310     private UIComponent parent;
311     
312     //private _ComponentAttributesMap attributesMap;
313 
314     @Override
315     public Map<String, Object> getAttributes()
316     {
317         // Since all components extending UILeaf are only transient references
318         // to text or instructions, we can do the following simplifications:  
319         // 1. Don't use reflection to retrieve properties, because it will never
320         // be done
321         // 2. Since the only key that will be saved here is MARK_ID, we can create
322         // a small map of size 2. In practice, this will prevent create a lot of
323         // maps, like the ones used on state helper
324         return this;
325     }
326 
327     @Override
328     public void clearInitialState()
329     {
330        //this component is transient, so it can be marked, because it does not have state!
331     }
332 
333     @Override
334     public boolean initialStateMarked()
335     {
336         //this component is transient, so it can be marked, because it does not have state!
337         return false;
338     }
339 
340     @Override
341     public void markInitialState()
342     {
343         //this component is transient, so no need to do anything
344     }
345 
346     
347     @Override
348     public void processEvent(ComponentSystemEvent event)
349             throws AbortProcessingException
350     {
351         //Do nothing, because UILeaf will not need to handle "binding" property
352     }
353 
354     @Override
355     @SuppressWarnings("deprecation")
356     public ValueBinding getValueBinding(String binding)
357     {
358         return null;
359     }
360 
361     @Override
362     @SuppressWarnings("deprecation")
363     public void setValueBinding(String name, ValueBinding binding)
364     {
365         // do nothing
366     }
367 
368     @Override
369     public ValueExpression getValueExpression(String name)
370     {
371         return null;
372     }
373 
374     @Override
375     public void setValueExpression(String name, ValueExpression arg1)
376     {
377         // do nothing
378     }
379 
380     public String getFamily()
381     {
382         return "facelets.LiteralText";
383     }
384 
385     @Override
386     public UIComponent getParent()
387     {
388         return this.parent;
389     }
390 
391     @Override
392     public void setParent(UIComponent parent)
393     {
394         this.parent = parent;
395     }
396 
397     @Override
398     public boolean isRendered()
399     {
400         return true;
401     }
402 
403     @Override
404     public void setRendered(boolean rendered)
405     {
406         // do nothing
407     }
408 
409     @Override
410     public String getRendererType()
411     {
412         return null;
413     }
414 
415     @Override
416     public void setRendererType(String rendererType)
417     {
418         // do nothing
419     }
420 
421     @Override
422     public boolean getRendersChildren()
423     {
424         return true;
425     }
426 
427     @Override
428     public List<UIComponent> getChildren()
429     {
430         List<UIComponent> children = Collections.emptyList();
431         return children;
432     }
433 
434     @Override
435     public int getChildCount()
436     {
437         return 0;
438     }
439 
440     @Override
441     public UIComponent findComponent(String id)
442     {
443         return null;
444     }
445 
446     @Override
447     public Map<String, UIComponent> getFacets()
448     {
449         return facets;
450     }
451 
452     @Override
453     public int getFacetCount()
454     {
455         return 0;
456     }
457 
458     @Override
459     public UIComponent getFacet(String name)
460     {
461         return null;
462     }
463 
464     @SuppressWarnings("unchecked")
465     @Override
466     public Iterator<UIComponent> getFacetsAndChildren()
467     {
468         // Performance: Collections.emptyList() is Singleton,
469         // but .iterator() creates new instance of AbstractList$Itr every invocation, because
470         // emptyList() extends AbstractList.  Therefore we cannot use Collections.emptyList() here. 
471         return EmptyIterator.INSTANCE;
472     }
473 
474     @Override
475     public void broadcast(FacesEvent event) throws AbortProcessingException
476     {
477         // do nothing
478     }
479 
480     @Override
481     public void decode(FacesContext faces)
482     {
483         // do nothing
484     }
485 
486     @Override
487     public void encodeBegin(FacesContext faces) throws IOException
488     {
489         // do nothing
490     }
491 
492     @Override
493     public void encodeChildren(FacesContext faces) throws IOException
494     {
495         // do nothing
496     }
497 
498     @Override
499     public void encodeEnd(FacesContext faces) throws IOException
500     {
501         // do nothing
502     }
503 
504     @Override
505     public void encodeAll(FacesContext faces) throws IOException
506     {
507         this.encodeBegin(faces);
508     }
509 
510     @Override
511     protected void addFacesListener(FacesListener faces)
512     {
513         // do nothing
514     }
515 
516     @Override
517     protected FacesListener[] getFacesListeners(Class faces)
518     {
519         return null;
520     }
521 
522     @Override
523     protected void removeFacesListener(FacesListener faces)
524     {
525         // do nothing
526     }
527 
528     @Override
529     public void queueEvent(FacesEvent event)
530     {
531         // do nothing
532     }
533 
534     @Override
535     public void processRestoreState(FacesContext faces, Object state)
536     {
537         // do nothing
538     }
539 
540     @Override
541     public void processDecodes(FacesContext faces)
542     {
543         // do nothing
544     }
545 
546     @Override
547     public void processValidators(FacesContext faces)
548     {
549         // do nothing
550     }
551 
552     @Override
553     public void processUpdates(FacesContext faces)
554     {
555         // do nothing
556     }
557 
558     @Override
559     public Object processSaveState(FacesContext faces)
560     {
561         return null;
562     }
563 
564     @Override
565     protected FacesContext getFacesContext()
566     {
567         return FacesContext.getCurrentInstance();
568     }
569 
570     @Override
571     protected Renderer getRenderer(FacesContext faces)
572     {
573         return null;
574     }
575 
576     public Object saveState(FacesContext faces)
577     {
578         return null;
579     }
580 
581     public void restoreState(FacesContext faces, Object state)
582     {
583         // do nothing
584     }
585 
586     public boolean isTransient()
587     {
588         return true;
589     }
590 
591     public void setTransient(boolean tranzient)
592     {
593         // do nothing
594     }
595 
596     @Override
597     public boolean invokeOnComponent(FacesContext context, String clientId, ContextCallback callback)
598             throws FacesException
599     {
600         //this component will never be a target for a callback, so always return false.
601         return false;
602     }
603 
604     @Override
605     public boolean visitTree(VisitContext context, VisitCallback callback)
606     {
607         // the visiting is complete and it shouldn't affect the visiting of the other
608         // children of the parent component, therefore return false
609         return false;
610     }
611 
612     //-------------- START ATTRIBUTE MAP IMPLEMENTATION ----------------
613 
614     private Map<String, Object> _attributes = null;
615     private String _markCreated = null;
616 
617     public void setMarkCreated(String markCreated)
618     {
619         _markCreated = markCreated;
620     }
621 
622     public int size()
623     {
624         return _attributes == null ? 0 : _attributes.size();
625     }
626          
627     public void clear()
628     {
629         if (_attributes != null)
630         {
631          _attributes.clear();
632         _markCreated = null;
633         }
634     }
635          
636     public boolean isEmpty()
637     {
638         if (_markCreated == null)
639         {
640             return _attributes == null ? false : _attributes.isEmpty();
641         }
642         else
643         {
644             return false;
645         }
646     }
647          
648     public boolean containsKey(Object key)
649     {
650         checkKey(key);
651 
652         if (ComponentSupport.MARK_CREATED.equals(key))
653         {
654             return _markCreated != null;
655         }
656         else
657         {
658             return (_attributes == null ? false :_attributes.containsKey(key));
659         }
660     }
661          
662     public boolean containsValue(Object value)
663     {
664         if (_markCreated != null && _markCreated.equals(value))
665         {
666             return true;
667         }
668         return (_attributes == null) ? false : _attributes.containsValue(value);
669     }
670 
671     public Collection<Object> values()
672     {
673         return getUnderlyingMap().values();
674     }
675 
676     public void putAll(Map<? extends String, ? extends Object> t)
677     {
678         for (Map.Entry<? extends String, ? extends Object> entry : t.entrySet())
679         {
680             put(entry.getKey(), entry.getValue());
681         }
682     }
683 
684     public Set<Entry<String, Object>> entrySet()
685     {
686         return getUnderlyingMap().entrySet();
687     }
688 
689     public Set<String> keySet()
690     {
691         return getUnderlyingMap().keySet();
692     }
693 
694     public Object get(Object key)
695     {
696         checkKey(key);
697 
698         if ("rendered".equals(key))
699         {
700             return true;
701         }
702         if ("transient".equals(key))
703         {
704             return true;
705         }
706         if (ComponentSupport.MARK_CREATED.equals(key))
707         {
708             return _markCreated;
709         }
710         return (_attributes == null) ? null : _attributes.get(key);
711     }
712 
713     public Object remove(Object key)
714     {
715         checkKey(key);
716 
717         if (ComponentSupport.MARK_CREATED.equals(key))
718         {
719             _markCreated = null;
720         }
721          return (_attributes == null) ? null : _attributes.remove(key);
722     }
723          
724     public Object put(String key, Object value)
725     {
726         checkKey(key);
727 
728         if (ComponentSupport.MARK_CREATED.equals(key))
729         {
730             String old = _markCreated;
731             _markCreated = (String) value;
732             return old;
733         }
734         return getUnderlyingMap().put(key, value);
735     }
736 
737     private void checkKey(Object key)
738     {
739         if (key == null)
740         {
741             throw new NullPointerException("key");
742         }
743         if (!(key instanceof String))
744         {
745             throw new ClassCastException("key is not a String");
746         }
747     }
748 
749     Map<String, Object> getUnderlyingMap()
750     {
751         if (_attributes == null)
752         {
753             _attributes = new HashMap<String, Object>(2,1);
754         }
755         return _attributes;
756     }
757 
758     @Override
759     public Map<String, Object> getPassThroughAttributes(boolean create)
760     {
761         if (create)
762         {
763             // Just return an empty map, because UILeaf cannot contain
764             // passthrough attributes.
765             return Collections.emptyMap();
766         }
767         else
768         {
769             return null;
770         }
771     }
772 
773 }