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 }