1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.myfaces.view.facelets.tag.jsf.html;
20
21 import java.util.Arrays;
22 import javax.faces.render.Renderer;
23 import javax.faces.view.facelets.FaceletException;
24 import javax.faces.view.facelets.Tag;
25 import javax.faces.view.facelets.TagAttribute;
26 import javax.faces.view.facelets.TagAttributes;
27 import javax.faces.view.facelets.TagDecorator;
28 import org.apache.myfaces.view.facelets.tag.TagAttributeImpl;
29 import org.apache.myfaces.view.facelets.tag.TagAttributesImpl;
30 import org.apache.myfaces.view.facelets.tag.jsf.JsfLibrary;
31 import org.apache.myfaces.view.facelets.tag.jsf.PassThroughLibrary;
32 import org.apache.myfaces.view.facelets.tag.jsf.core.CoreLibrary;
33
34
35
36
37
38
39
40
41 public class DefaultTagDecorator implements TagDecorator
42 {
43 public final static String XHTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
44 public final static String JSF_NAMESPACE = JsfLibrary.NAMESPACE;
45 public final static String JSF_ALIAS_NAMESPACE = JsfLibrary.ALIAS_NAMESPACE;
46 public final static String PASS_THROUGH_NAMESPACE = PassThroughLibrary.NAMESPACE;
47 public final static String PASS_THROUGH_ALIAS_NAMESPACE = PassThroughLibrary.ALIAS_NAMESPACE;
48 private final static String EMPTY_NAMESPACE = "";
49
50 private final static String P_ELEMENTNAME = "p:"+Renderer.PASSTHROUGH_RENDERER_LOCALNAME_KEY;
51
52
53
54
55 static private final Object[][] LOCAL_NAME_ARR = new Object[256][];
56
57 static private final Object[] A_NAMES = new Object[]
58 {
59 "a", new Object[]
60 {
61 new TagSelectorImpl("jsf:action", "h:commandLink"),
62 new TagSelectorImpl("jsf:actionListener", "h:commandLink"),
63 new TagSelectorImpl("jsf:value", "h:outputLink"),
64 new TagSelectorImpl("jsf:outcome", "h:link")
65 }
66 };
67
68 static private final Object[] B_NAMES = new Object[]
69 {
70 "body", new Object[]{new TagSelectorImpl(null, "h:body")},
71 "button", new Object[]{
72 new TagSelectorImpl("jsf:outcome", "h:button"),
73 new TagSelectorImpl(null, "h:commandButton")
74 }
75 };
76
77 static private final Object[] F_NAMES = new Object[]
78 {
79 "form", new Object[]{new TagSelectorImpl(null, "h:form")}
80 };
81
82 static private final Object[] H_NAMES = new Object[]
83 {
84 "head", new Object[]{new TagSelectorImpl(null, "h:head")}
85 };
86
87 static private final Object[] I_NAMES = new Object[]
88 {
89 "img", new Object[]{new TagSelectorImpl(null, "h:graphicImage")},
90
91
92
93 "input", new Object[]{
94 new TagSelectorImpl("type=\"button\"", "h:commandButton"),
95 new TagSelectorImpl("type=\"checkbox\"", "h:selectBooleanCheckbox"),
96
97 new TagSelectorImpl("type=\"color\"", "h:inputText"),
98 new TagSelectorImpl("type=\"date\"", "h:inputText"),
99 new TagSelectorImpl("type=\"datetime\"", "h:inputText"),
100 new TagSelectorImpl("type=\"datetime-local\"", "h:inputText"),
101 new TagSelectorImpl("type=\"email\"", "h:inputText"),
102 new TagSelectorImpl("type=\"month\"", "h:inputText"),
103 new TagSelectorImpl("type=\"number\"", "h:inputText"),
104 new TagSelectorImpl("type=\"range\"", "h:inputText"),
105 new TagSelectorImpl("type=\"search\"", "h:inputText"),
106 new TagSelectorImpl("type=\"time\"", "h:inputText"),
107 new TagSelectorImpl("type=\"url\"", "h:inputText"),
108 new TagSelectorImpl("type=\"week\"", "h:inputText"),
109
110 new TagSelectorImpl("type=\"file\"", "h:inputFile"),
111 new TagSelectorImpl("type=\"hidden\"", "h:inputHidden"),
112 new TagSelectorImpl("type=\"password\"", "h:inputSecret"),
113 new TagSelectorImpl("type=\"reset\"", "h:commandButton"),
114 new TagSelectorImpl("type=\"submit\"", "h:commandButton"),
115 new TagSelectorImpl("type=\"*\"", "h:inputText")
116 }
117 };
118
119 static private final Object[] L_NAMES = new Object[]
120 {
121 "label", new Object[]{new TagSelectorImpl(null, "h:outputLabel")},
122 "link", new Object[]{new TagSelectorImpl(null, "h:outputStylesheet")}
123 };
124
125 static private final Object[] S_NAMES = new Object[]
126 {
127 "script", new Object[]{new TagSelectorImpl(null, "h:outputScript")},
128 "select", new Object[]
129 {
130 new TagSelectorImpl("multiple=\"*\"", "h:selectManyListbox"),
131 new TagSelectorImpl(null, "h:selectOneListbox")
132 }
133 };
134
135 static private final Object[] T_NAMES = new Object[]
136 {
137 "textarea", new Object[]{new TagSelectorImpl(null, "h:inputTextarea")}
138 };
139
140 static
141 {
142 LOCAL_NAME_ARR['a'] = A_NAMES;
143 LOCAL_NAME_ARR['A'] = A_NAMES;
144 LOCAL_NAME_ARR['b'] = B_NAMES;
145 LOCAL_NAME_ARR['B'] = B_NAMES;
146 LOCAL_NAME_ARR['f'] = F_NAMES;
147 LOCAL_NAME_ARR['F'] = F_NAMES;
148 LOCAL_NAME_ARR['h'] = H_NAMES;
149 LOCAL_NAME_ARR['H'] = H_NAMES;
150 LOCAL_NAME_ARR['i'] = I_NAMES;
151 LOCAL_NAME_ARR['I'] = I_NAMES;
152 LOCAL_NAME_ARR['l'] = L_NAMES;
153 LOCAL_NAME_ARR['L'] = L_NAMES;
154 LOCAL_NAME_ARR['s'] = S_NAMES;
155 LOCAL_NAME_ARR['S'] = S_NAMES;
156 LOCAL_NAME_ARR['t'] = T_NAMES;
157 LOCAL_NAME_ARR['T'] = T_NAMES;
158 }
159
160 static private final String[][] RESERVED_JSF_ATTRS_ARR = new String[256][];
161
162 static private final String[] JSF_ATTRS_B_NAMES = {"binding"};
163
164 static private final String[] JSF_ATTRS_I_NAMES = {"id"};
165
166 static private final String[] JSF_ATTRS_R_NAMES = {"rendered"};
167
168 static private final String[] JSF_ATTRS_T_NAMES = {"transient"};
169
170 static
171 {
172 RESERVED_JSF_ATTRS_ARR['b'] = JSF_ATTRS_B_NAMES;
173 RESERVED_JSF_ATTRS_ARR['B'] = JSF_ATTRS_B_NAMES;
174 RESERVED_JSF_ATTRS_ARR['i'] = JSF_ATTRS_I_NAMES;
175 RESERVED_JSF_ATTRS_ARR['I'] = JSF_ATTRS_I_NAMES;
176 RESERVED_JSF_ATTRS_ARR['r'] = JSF_ATTRS_R_NAMES;
177 RESERVED_JSF_ATTRS_ARR['R'] = JSF_ATTRS_R_NAMES;
178 RESERVED_JSF_ATTRS_ARR['t'] = JSF_ATTRS_T_NAMES;
179 RESERVED_JSF_ATTRS_ARR['T'] = JSF_ATTRS_T_NAMES;
180 }
181
182 private static final TagDecoratorExecutor NO_MATCH_SELECTOR = new TagSelectorImpl(null, "jsf:element");
183
184 public Tag decorate(Tag tag)
185 {
186 boolean jsfNamespaceFound = false;
187
188 for (String namespace : tag.getAttributes().getNamespaces())
189 {
190 if (JSF_NAMESPACE.equals(namespace) || JSF_ALIAS_NAMESPACE.equals(namespace))
191 {
192 jsfNamespaceFound = true;
193 break;
194 }
195 }
196 if (!jsfNamespaceFound)
197 {
198
199 return null;
200 }
201
202
203
204 if (EMPTY_NAMESPACE.equals(tag.getNamespace()) ||
205 XHTML_NAMESPACE.equals(tag.getNamespace()))
206 {
207 String localName = tag.getLocalName();
208 boolean processed = false;
209 if (isLocalNameDecorated(localName))
210 {
211 Object[] array = LOCAL_NAME_ARR[localName.charAt(0)];
212 int localNameIndex = -1;
213 if (array != null)
214 {
215 for (int i = array.length - 2; i >= 0; i-=2)
216 {
217 if (localName.equalsIgnoreCase((String)array[i]))
218 {
219 localNameIndex = i;
220 break;
221 }
222 }
223 if (localNameIndex >= 0)
224 {
225 Object[] tagSelectorArray = (Object[]) array[localNameIndex+1];
226
227 for (int i = 0; i < tagSelectorArray.length; i++)
228 {
229 TagSelector tagSelector = (TagSelector) tagSelectorArray[i];
230 TagDecoratorExecutor executor = tagSelector.getExecutorIfApplies(tag);
231
232 if (executor != null)
233 {
234 return executor.decorate(tag, convertTagAttributes(tag));
235 }
236 }
237 }
238 }
239 }
240 if (!processed)
241 {
242
243 return NO_MATCH_SELECTOR.decorate(tag, convertTagAttributes(tag));
244 }
245 return null;
246 }
247 else
248 {
249 throw new FaceletException("Attributes under "+JSF_NAMESPACE+
250 " can only be used for tags under "+ XHTML_NAMESPACE +" or tags with no namespace defined" );
251 }
252 }
253
254 private TagAttributes convertTagAttributes(Tag tag)
255 {
256 TagAttribute[] sourceTagAttributes = tag.getAttributes().getAll();
257
258 String elementNameTagLocalName = tag.getLocalName();
259
260 TagAttribute elementNameTagAttribute = new TagAttributeImpl(
261 tag.getLocation(), PASS_THROUGH_NAMESPACE , Renderer.PASSTHROUGH_RENDERER_LOCALNAME_KEY,
262 P_ELEMENTNAME, elementNameTagLocalName );
263
264
265 int duplicateCount = 0;
266
267 TagAttribute[] convertedTagAttributes = new TagAttribute[
268 sourceTagAttributes.length+1+duplicateCount];
269 boolean elementNameTagAttributeSet = false;
270 int j = 0;
271
272 for (int i = 0; i < sourceTagAttributes.length; i++)
273 {
274 TagAttribute tagAttribute = sourceTagAttributes[i];
275 String convertedNamespace;
276 String qname;
277 String namespace = tagAttribute.getNamespace();
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311 if (JSF_NAMESPACE.equals(namespace) || JSF_ALIAS_NAMESPACE.equals(namespace))
312 {
313
314
315
316
317 convertedNamespace = "";
318 qname = tagAttribute.getLocalName();
319
320 convertedTagAttributes[j] = new TagAttributeImpl(tagAttribute.getLocation(),
321 convertedNamespace, tagAttribute.getLocalName(), qname, tagAttribute.getValue());
322 }
323 else if (namespace == null)
324 {
325
326
327
328
329
330
331 convertedNamespace = PASS_THROUGH_NAMESPACE;
332 qname = "p:"+tagAttribute.getLocalName();
333
334 convertedTagAttributes[j] = new TagAttributeImpl(tagAttribute.getLocation(),
335 convertedNamespace, tagAttribute.getLocalName(), qname, tagAttribute.getValue());
336 }
337 else if (tagAttribute.getNamespace().length() == 0)
338 {
339
340
341
342
343
344
345
346 convertedNamespace = PASS_THROUGH_NAMESPACE;
347 qname = "p:"+tagAttribute.getLocalName();
348
349 convertedTagAttributes[j] = new TagAttributeImpl(tagAttribute.getLocation(),
350 convertedNamespace, tagAttribute.getLocalName(), qname, tagAttribute.getValue());
351 }
352 else
353 {
354
355
356 convertedTagAttributes[j] = tagAttribute;
357 }
358
359 if (Renderer.PASSTHROUGH_RENDERER_LOCALNAME_KEY.equals(convertedTagAttributes[j].getLocalName()) && (
360 PASS_THROUGH_NAMESPACE.equals(convertedTagAttributes[j].getNamespace()) ||
361 PASS_THROUGH_ALIAS_NAMESPACE.equals(convertedTagAttributes[j].getNamespace()) ) )
362 {
363 elementNameTagAttributeSet = true;
364 }
365 j++;
366 }
367
368 if (elementNameTagAttributeSet)
369 {
370
371 return new TagAttributesImpl(Arrays.copyOf(convertedTagAttributes, convertedTagAttributes.length-1));
372 }
373 else
374 {
375 convertedTagAttributes[convertedTagAttributes.length-1] = elementNameTagAttribute;
376 return new TagAttributesImpl(convertedTagAttributes);
377 }
378 }
379
380 private boolean isLocalNameDecorated(String elem)
381 {
382 Object[] array = LOCAL_NAME_ARR[elem.charAt(0)];
383 if (array != null)
384 {
385 for (int i = array.length - 2; i >= 0; i-=2)
386 {
387 if (elem.equalsIgnoreCase((String)array[i]))
388 {
389 return true;
390 }
391 }
392 }
393 return false;
394 }
395
396 private boolean isReservedJSFAttribute(String attr)
397 {
398 String[] array = RESERVED_JSF_ATTRS_ARR[attr.charAt(0)];
399 if (array != null)
400 {
401 for (int i = array.length - 1; i >= 0; i-=1)
402 {
403 if (attr.equalsIgnoreCase((String)array[i]))
404 {
405 return true;
406 }
407 }
408 }
409 return false;
410 }
411
412 private static interface TagDecoratorExecutor
413 {
414 public Tag decorate(Tag orig, TagAttributes attributes);
415
416 }
417
418 private static abstract class TagSelector
419 {
420 public abstract TagDecoratorExecutor getExecutorIfApplies(Tag tag);
421 }
422
423 private static class TagSelectorImpl extends TagSelector implements TagDecoratorExecutor
424 {
425
426 private String attributeQName;
427 private String attributeLocalName;
428 private String attributePrefix;
429 private final String attributeNamespace;
430 private final String attributeAliasNamespace;
431 private String matchValue;
432
433 private String targetQName;
434 private String targetNamespace;
435 private String targetLocalName;
436
437 public TagSelectorImpl(String selector, String targetQName)
438 {
439
440
441
442 if (selector != null)
443 {
444 int i = selector.indexOf('=');
445 if (i >= 0)
446 {
447 this.attributeQName = selector.substring(0,i);
448 String value = selector.substring(i+1);
449 int s = value.indexOf('"');
450 int t = value.lastIndexOf('"');
451 if (s >= 0 && t >= 0 && t > s)
452 {
453 this.matchValue = value.substring(s+1,t);
454 }
455 else
456 {
457 this.matchValue = value;
458 }
459 }
460 else
461 {
462 this.attributeQName = selector;
463 this.matchValue = null;
464 }
465
466 int j = attributeQName.indexOf(':');
467 this.attributeLocalName = (j >= 0) ? attributeQName.substring(j+1) : attributeQName;
468 this.attributePrefix = (j >= 0) ? attributeQName.substring(0, j) : null;
469 this.attributeNamespace = resolveSelectorNamespace(this.attributePrefix);
470 this.attributeAliasNamespace = resolveAliasSelectorNamespace(this.attributePrefix);
471 }
472 else
473 {
474 this.attributeQName = null;
475 this.matchValue = null;
476 this.attributeLocalName = null;
477 this.attributePrefix = null;
478 this.attributeNamespace = "";
479 this.attributeAliasNamespace = null;
480 }
481
482 this.targetQName = targetQName;
483 if (targetQName != null)
484 {
485 int j = targetQName.indexOf(':');
486 if (j >= 0)
487 {
488
489 if (j == 1 && targetQName.charAt(0) == 'h')
490 {
491 this.targetNamespace = HtmlLibrary.NAMESPACE;
492 this.targetLocalName = targetQName.substring(j+1);
493 }
494 else if (j == 3 && targetQName.startsWith("jsf"))
495 {
496 this.targetNamespace = JsfLibrary.ALIAS_NAMESPACE;
497 this.targetLocalName = targetQName.substring(j+1);
498 }
499 }
500 else
501 {
502 this.targetLocalName = targetQName;
503 }
504 }
505 }
506
507 public TagDecoratorExecutor getExecutorIfApplies(Tag tag)
508 {
509 if (attributeQName != null)
510 {
511 if (matchValue != null)
512 {
513 String attributeNS = attributeNamespace;
514 TagAttribute attr = tag.getAttributes().get(attributeNS, attributeLocalName);
515 if (attr == null && attributeAliasNamespace.length() > 0)
516 {
517 attributeNS = attributeAliasNamespace;
518 attr = tag.getAttributes().get(attributeAliasNamespace, attributeLocalName);
519 }
520 if (attr != null)
521 {
522 if (attributeNS.equals(attr.getNamespace()) )
523 {
524
525 if (matchValue.equals(attr.getValue()))
526 {
527 return this;
528 }
529 else if ("*".equals(matchValue) && attr.getValue() != null)
530 {
531 return this;
532 }
533 }
534 else if (attributeNS == "" && attr.getNamespace() == null)
535 {
536
537 if (matchValue.equals(attr.getValue()))
538 {
539 return this;
540 }
541 else if ("*".equals(matchValue) && attr.getValue() != null)
542 {
543 return this;
544 }
545 }
546 }
547 }
548 else
549 {
550 String attributeNS = attributeNamespace;
551 TagAttribute attr = tag.getAttributes().get(attributeNS, attributeLocalName);
552 if (attr == null)
553 {
554 attributeNS = attributeAliasNamespace;
555 attr = tag.getAttributes().get(attributeNS, attributeLocalName);
556 }
557 if (attr != null)
558 {
559 if (attributeNS.equals(attr.getNamespace()))
560 {
561
562 return this;
563 }
564 else if (attributeNS == "" && attr.getNamespace() == null)
565 {
566
567 return this;
568 }
569 }
570 }
571 return null;
572 }
573 else
574 {
575 return this;
576 }
577 }
578
579 public Tag decorate(Tag orig, TagAttributes attributes)
580 {
581 return new Tag(orig.getLocation(), this.targetNamespace,
582 this.targetLocalName, this.targetQName, attributes);
583 }
584 }
585
586 private static String resolveSelectorNamespace(String prefix)
587 {
588 if ("jsf".equals(prefix))
589 {
590 return JsfLibrary.NAMESPACE;
591 }
592 else if ("h".equals(prefix))
593 {
594 return HtmlLibrary.NAMESPACE;
595 }
596 else if ("f".equals(prefix))
597 {
598 return CoreLibrary.NAMESPACE;
599 }
600 return "";
601 }
602
603 private static String resolveAliasSelectorNamespace(String prefix)
604 {
605 if ("jsf".equals(prefix))
606 {
607 return JsfLibrary.ALIAS_NAMESPACE;
608 }
609 else if ("h".equals(prefix))
610 {
611 return HtmlLibrary.ALIAS_NAMESPACE;
612 }
613 else if ("f".equals(prefix))
614 {
615 return CoreLibrary.ALIAS_NAMESPACE;
616 }
617 return "";
618 }
619 }