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.util.HashSet;
22  import java.util.Set;
23  
24  import javax.faces.component.UIComponent;
25  import javax.faces.context.FacesContext;
26  import javax.faces.event.ExceptionQueuedEvent;
27  import javax.faces.event.ExceptionQueuedEventContext;
28  import javax.faces.view.Location;
29  
30  import org.apache.myfaces.shared.renderkit.RendererUtils;
31  
32  /**
33   *
34   * @author Leonardo Uribe
35   */
36  public final class CheckDuplicateIdFaceletUtils
37  {
38      
39      public static void checkIdsStatefulComponents (FacesContext context, UIComponent view)
40      {
41          checkIdsStatefulComponents (context, view, new HashSet<String>());
42      }
43  
44      private static void checkIdsStatefulComponents (FacesContext context, 
45              UIComponent component, Set<String> existingIds)
46      {
47          String id;
48          
49          if (component == null)
50          {
51              return;
52          }
53          
54          // Need to use this form of the client ID method so we generate the client-side ID.
55          
56          id = component.getClientId (context);
57          
58          if (!existingIds.add (id))
59          {
60              DuplicateIdException duplicateIdException = createAndQueueException(context, component, id);
61              throw duplicateIdException;
62          }
63          
64          int facetCount = component.getFacetCount();
65          if (facetCount > 0)
66          {
67              for (UIComponent facet : component.getFacets().values())
68              {
69                  if (!(facet instanceof UILeaf))
70                  {
71                      checkIdsStatefulComponents (context, facet, existingIds);
72                  }
73              }
74          }
75          for (int i = 0, childCount = component.getChildCount(); i < childCount; i++)
76          {
77              UIComponent child = component.getChildren().get(i);
78              if (!(child instanceof UILeaf))
79              {
80                  checkIdsStatefulComponents (context, child, existingIds);
81              }
82          }
83      }
84  
85      public static void checkIds (FacesContext context, UIComponent view)
86      {
87          checkIds (context, view, new HashSet<String>());
88      }
89      
90      private static void checkIds (FacesContext context, UIComponent component, Set<String> existingIds)
91      {
92          String id;
93          
94          if (component == null)
95          {
96              return;
97          }
98          
99          // Need to use this form of the client ID method so we generate the client-side ID.
100         
101         id = component.getClientId (context);
102         
103         if (!existingIds.add (id))
104         {
105             DuplicateIdException duplicateIdException = createAndQueueException(context, component, id);
106             throw duplicateIdException;
107         }
108         
109         int facetCount = component.getFacetCount();
110         if (facetCount > 0)
111         {
112             for (UIComponent facet : component.getFacets().values())
113             {
114                 checkIds (context, facet, existingIds);
115             }
116         }
117         for (int i = 0, childCount = component.getChildCount(); i < childCount; i++)
118         {
119             UIComponent child = component.getChildren().get(i);
120             checkIds (context, child, existingIds);
121         }
122     }
123 
124     private static DuplicateIdException createAndQueueException(FacesContext context, UIComponent component, String id)
125     {
126         String message = "Component with duplicate id \"" + id + "\" found. The first component is ";
127 
128         
129         // We report as problematic the second component. The client (an exception handler mostly)
130         // has the possibility to report all about the second component, 
131         // but the first component is hard to find, especially in large view with tons of naming containers
132         // So we do here two things:
133         // 1) provide an info about the first component in exception message 
134         UIComponent firstComponent = context.getViewRoot().findComponent(id);
135         Location location = (Location) firstComponent.getAttributes().get(UIComponent.VIEW_LOCATION_KEY);
136         if (location != null)
137         {
138             message += location.toString();
139         }
140         else
141         {
142             // location is not available in production mode or if the component
143             // doesn't come from Facelets VDL.
144             message += RendererUtils.getPathToComponent(firstComponent);
145         }
146         
147         // 2) we store the first commponent in exception attributes
148         DuplicateIdException duplicateIdException = new DuplicateIdException 
149                 (message, firstComponent, component);
150         
151         ExceptionQueuedEventContext exceptionContext 
152         = new ExceptionQueuedEventContext(context, duplicateIdException,
153                 component, context.getCurrentPhaseId());
154 
155         
156         context.getApplication().publishEvent(context, ExceptionQueuedEvent.class, exceptionContext);
157         return duplicateIdException;
158     }
159 }