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