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