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