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.trinidad.change;
20  
21  import java.util.List;
22  
23  import javax.faces.component.NamingContainer;
24  import javax.faces.component.UIComponent;
25  
26  import org.apache.myfaces.trinidad.logging.TrinidadLogger;
27  
28  import org.w3c.dom.Element;
29  import org.w3c.dom.NamedNodeMap;
30  import org.w3c.dom.Node;
31  
32  
33  /**
34   * Utility functions for use by Changes.
35   * @version $Name:  $ ($Revision: adfrt/faces/adf-faces-api/src/main/java/oracle/adf/view/faces/change/ChangeUtils.java#0 $) $Date: 10-nov-2005.19:09:58 $
36   */
37  final class ChangeUtils 
38  {
39    private ChangeUtils()
40    {
41    }
42  
43    /**
44     * Given a parent component and the identifier for the child, looks up among
45     *  the children for a child with the specified identifier and returns.
46     * Returns null if there were to be no such child
47     * @param parent the parent UIComponent
48     * @param childId the 'id' identifier value of child to be searched in the parent's 
49     *        children.
50     */
51    @SuppressWarnings("unchecked")
52    public static UIComponent getChildForId(UIComponent parent, String childId)
53    {
54      return getChildForId(parent, childId, "id");
55    }
56    
57    /**
58     * Given a parent component and the identifier value for the child, looks up among
59     * the children for a child with the specified identifier and returns.
60     * Returns null if there were to be no such child
61     * @param parent the parent UIComponent
62     * @param childId the identifier value of child to be searched in the parent's 
63     *        children.
64     * @param identifier the identifier type 
65     */
66    @SuppressWarnings("unchecked")
67    public static UIComponent getChildForId(
68      UIComponent parent, 
69      String childId,
70      String identifier)
71    {
72      if (parent == null)
73        return null;
74  
75      int numChildren = parent.getChildCount();
76      if (numChildren == 0)
77        return null;
78  
79      List<UIComponent> children = parent.getChildren();
80      
81      for (int i=0; i<numChildren; i++)
82      {
83        UIComponent child = children.get(i);
84        Object attrVal = child.getAttributes().get(identifier);
85        
86        if ( childId.equals(attrVal) )
87          return child;
88      }
89      return null;
90    }
91    
92    /**
93     * Given a parent component and the identifier for the child, looks up among
94     * the children for a child with the specified identifier and returns the index
95     * of the child
96     * Returns -1 if there were to be no such child
97     * @param parent
98     * @param childId the identifier of child to be searched in the parent's 
99     * children
100    */
101   @SuppressWarnings("unchecked")
102   public static int getChildIndexForId(UIComponent parent, String childId)
103   {
104     if (parent == null)
105       throw new NullPointerException(_LOG.getMessage(
106         "PARENT_CANNOT_BE_NULL"));
107 
108     int numChildren = parent.getChildCount();
109     if (numChildren == 0)
110       return -1;
111 
112     List<UIComponent> children = parent.getChildren();      
113     UIComponent child;    
114     for (int i=0; i<numChildren; i++)
115     {
116       child = children.get(i);
117       if ( childId.equals(child.getId()) )
118         return i;
119     }
120     return -1;
121   }
122   
123   /**
124    * Search the supplied Node and its descendants for an Element Node with the 
125    * scopedTargetId.
126    * @param baseNode The base Node of the subtree relative to which the target
127    * Node is to be found.
128    * @param scopedTargetId The scoped id of the target node which is to be found. 
129    * This id should be relative from the base Node of the search, with 
130    * NamingContainer.SEPARATOR_CHAR being the separator for fragments. If the
131    * targetId starts with a NamingContainer.SEPARATOR_CHAR, it is considered
132    * as an absolute id, and the owner Document will be the base Node of search.
133    * Examples of scopedTargetId values: 'foo:bar:baz'/':foo:bar:baz'/'foo'.
134    * @param searchDepth The integer which indicates till how many levels deeper 
135    * from the baseNode, the search has to be performed.
136    * @return The target Node with the given scopedTargetId if found within the
137    * permitted searchDepth, else null 
138    */
139   static Node __findNodeByScopedId(
140     Node baseNode,
141     String scopedTargetId,
142     int searchDepth)
143   {
144     if (baseNode == null || 
145         scopedTargetId == null || 
146         scopedTargetId.length() == 0)
147       return null;
148      
149     // Check if we have received an absolute id.
150     if (NamingContainer.SEPARATOR_CHAR == scopedTargetId.charAt(0))
151     {
152       // If so directly deal with the owner Document.
153       if (baseNode.getNodeType() != Node.DOCUMENT_NODE)
154         baseNode = baseNode.getOwnerDocument();
155 
156       // Remove leading ':'
157       scopedTargetId = scopedTargetId.substring(1);
158     }
159 
160     // 'foo:bar:baz' -> ['foo'],['bar'},['baz']
161     String[] idFrags = 
162       scopedTargetId.split(String.valueOf(NamingContainer.SEPARATOR_CHAR));
163     
164     return _traceNodeByIdPath(baseNode, idFrags, 0, searchDepth);
165   }
166   
167   /**
168    * Given a node representing a component, returns the named facet's Element.
169    * @param componentNode The node to search for a facet contained in it.
170    * @param facetName The name of the facet to search for.
171    * @return
172    */
173   static Element __getFacetElement(
174     Node componentNode,
175     String facetName)
176   {
177     assert componentNode != null;
178     assert (facetName != null) && (facetName.length() > 0);
179     
180     Node currChild = componentNode.getFirstChild();
181     
182     while (currChild != null)
183     {
184       // check for local name match
185       if ("facet".equals(currChild.getLocalName()))
186       {
187         // check for namespace match
188         if (__JSF_CORE_NAMESPACE.equals(currChild.getNamespaceURI()))
189         {
190           NamedNodeMap attributes = currChild.getAttributes();
191 
192           if (facetName.equals(attributes.getNamedItem("name").getNodeValue()))
193           {
194             return (Element)currChild;
195           }
196         }
197       }
198 
199       currChild = currChild.getNextSibling();
200     }
201     
202     return null;
203   }
204 
205   /**
206    * Removes all of the children from the parent Node.
207    * @param parentNode 
208    */
209   static void __removeAllChildren(Node parentNode)
210   {
211     Node nukeChild = parentNode.getFirstChild();
212     
213     while (nukeChild != null)
214     {
215       parentNode.removeChild(nukeChild);
216       nukeChild = parentNode.getFirstChild();
217     }
218   }
219 
220   /**
221    * Traces for the targetNode recursively in the subtree rooted by baseNode.
222    * Trace path is also controlled by the id path fragments and the fragIndex. 
223    * Trace stops when the permitted searchDepth is reached, or when the trace 
224    * faled.
225    * @returns The traget node of the search. Returns null if the target Node is
226    * not to be found, or if we have exceeded the search depth.
227    */
228   private static Node _traceNodeByIdPath(
229     Node baseNode,
230     String[] idFrags,
231     int fragIndex,
232     int searchDepth)
233   {
234     if ((baseNode.getNodeType() == Node.ELEMENT_NODE) && 
235         idFrags[fragIndex].equals(((Element)baseNode).getAttribute(_ID_ATTRIB_NAME)))
236     {
237       if (idFrags.length == fragIndex + 1)
238       {
239         // This is the node for the last of the id fragments, so we found the 
240         // target now.
241         return baseNode;
242       }
243       else 
244       {
245         // This is the intermediate node matching the path, start looking for 
246         //  nodes with id's matching rest of fragments.
247         fragIndex++;
248       }
249     }
250 
251     // Check child Nodes
252     if (searchDepth > 0)
253     {
254       searchDepth--;
255 
256       Node currChild  = baseNode.getFirstChild();
257      
258       while (currChild != null)
259       {
260         if (Node.ELEMENT_NODE == currChild.getNodeType())
261         {
262           Node targetNode = _traceNodeByIdPath(currChild,
263                                                idFrags,
264                                                fragIndex,
265                                                searchDepth);
266           if (targetNode != null)
267             return targetNode;
268         }
269 
270         currChild = currChild.getNextSibling();
271       }
272     }
273     
274     // We are past the permitted search depth, or we searched the entire subtree 
275     // in vain, abort.
276     return null;
277   }
278 
279   static final String __JSF_CORE_NAMESPACE = "http://java.sun.com/jsf/core";
280   private static final String _ID_ATTRIB_NAME = "id";
281   private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(
282     ChangeUtils.class);
283 }