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 @Override
185 public Tag decorate(Tag tag)
186 {
187 boolean jsfNamespaceFound = false;
188
189 for (String namespace : tag.getAttributes().getNamespaces())
190 {
191 if (JSF_NAMESPACE.equals(namespace) || JSF_ALIAS_NAMESPACE.equals(namespace))
192 {
193 jsfNamespaceFound = true;
194 break;
195 }
196 }
197 if (!jsfNamespaceFound)
198 {
199
200 return null;
201 }
202
203
204
205 if (EMPTY_NAMESPACE.equals(tag.getNamespace()) ||
206 XHTML_NAMESPACE.equals(tag.getNamespace()))
207 {
208 String localName = tag.getLocalName();
209 boolean processed = false;
210 if (isLocalNameDecorated(localName))
211 {
212 Object[] array = LOCAL_NAME_ARR[localName.charAt(0)];
213 int localNameIndex = -1;
214 if (array != null)
215 {
216 for (int i = array.length - 2; i >= 0; i-=2)
217 {
218 if (localName.equalsIgnoreCase((String)array[i]))
219 {
220 localNameIndex = i;
221 break;
222 }
223 }
224 if (localNameIndex >= 0)
225 {
226 Object[] tagSelectorArray = (Object[]) array[localNameIndex+1];
227
228 for (int i = 0; i < tagSelectorArray.length; i++)
229 {
230 TagSelector tagSelector = (TagSelector) tagSelectorArray[i];
231 TagDecoratorExecutor executor = tagSelector.getExecutorIfApplies(tag);
232
233 if (executor != null)
234 {
235 return executor.decorate(tag, convertTagAttributes(tag));
236 }
237 }
238 }
239 }
240 }
241 if (!processed)
242 {
243
244 return NO_MATCH_SELECTOR.decorate(tag, convertTagAttributes(tag));
245 }
246 return null;
247 }
248 else
249 {
250 throw new FaceletException("Attributes under "+JSF_NAMESPACE+
251 " can only be used for tags under "+ XHTML_NAMESPACE +" or tags with no namespace defined" );
252 }
253 }
254
255 private TagAttributes convertTagAttributes(Tag tag)
256 {
257 TagAttribute[] sourceTagAttributes = tag.getAttributes().getAll();
258
259 String elementNameTagLocalName = tag.getLocalName();
260
261 TagAttribute elementNameTagAttribute = new TagAttributeImpl(
262 tag.getLocation(), PASS_THROUGH_NAMESPACE , Renderer.PASSTHROUGH_RENDERER_LOCALNAME_KEY,
263 P_ELEMENTNAME, elementNameTagLocalName );
264
265
266 int duplicateCount = 0;
267
268 TagAttribute[] convertedTagAttributes = new TagAttribute[
269 sourceTagAttributes.length+1+duplicateCount];
270 boolean elementNameTagAttributeSet = false;
271 int j = 0;
272
273 for (int i = 0; i < sourceTagAttributes.length; i++)
274 {
275 TagAttribute tagAttribute = sourceTagAttributes[i];
276 String convertedNamespace;
277 String qname;
278 String namespace = tagAttribute.getNamespace();
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
312 if (JSF_NAMESPACE.equals(namespace) || JSF_ALIAS_NAMESPACE.equals(namespace))
313 {
314
315
316
317
318 convertedNamespace = "";
319 qname = tagAttribute.getLocalName();
320
321 convertedTagAttributes[j] = new TagAttributeImpl(tagAttribute.getLocation(),
322 convertedNamespace, tagAttribute.getLocalName(), qname, tagAttribute.getValue());
323 }
324 else if (namespace == null)
325 {
326
327
328
329
330
331
332 convertedNamespace = PASS_THROUGH_NAMESPACE;
333 qname = "p:"+tagAttribute.getLocalName();
334
335 convertedTagAttributes[j] = new TagAttributeImpl(tagAttribute.getLocation(),
336 convertedNamespace, tagAttribute.getLocalName(), qname, tagAttribute.getValue());
337 }
338 else if (tagAttribute.getNamespace().length() == 0)
339 {
340
341
342
343
344
345
346
347 convertedNamespace = PASS_THROUGH_NAMESPACE;
348 qname = "p:"+tagAttribute.getLocalName();
349
350 convertedTagAttributes[j] = new TagAttributeImpl(tagAttribute.getLocation(),
351 convertedNamespace, tagAttribute.getLocalName(), qname, tagAttribute.getValue());
352 }
353 else
354 {
355
356
357 convertedTagAttributes[j] = tagAttribute;
358 }
359
360 if (Renderer.PASSTHROUGH_RENDERER_LOCALNAME_KEY.equals(convertedTagAttributes[j].getLocalName()) && (
361 PASS_THROUGH_NAMESPACE.equals(convertedTagAttributes[j].getNamespace()) ||
362 PASS_THROUGH_ALIAS_NAMESPACE.equals(convertedTagAttributes[j].getNamespace()) ) )
363 {
364 elementNameTagAttributeSet = true;
365 }
366 j++;
367 }
368
369 if (elementNameTagAttributeSet)
370 {
371
372 return new TagAttributesImpl(Arrays.copyOf(convertedTagAttributes, convertedTagAttributes.length-1));
373 }
374 else
375 {
376 convertedTagAttributes[convertedTagAttributes.length-1] = elementNameTagAttribute;
377 return new TagAttributesImpl(convertedTagAttributes);
378 }
379 }
380
381 private boolean isLocalNameDecorated(String elem)
382 {
383 Object[] array = LOCAL_NAME_ARR[elem.charAt(0)];
384 if (array != null)
385 {
386 for (int i = array.length - 2; i >= 0; i-=2)
387 {
388 if (elem.equalsIgnoreCase((String)array[i]))
389 {
390 return true;
391 }
392 }
393 }
394 return false;
395 }
396
397 private boolean isReservedJSFAttribute(String attr)
398 {
399 String[] array = RESERVED_JSF_ATTRS_ARR[attr.charAt(0)];
400 if (array != null)
401 {
402 for (int i = array.length - 1; i >= 0; i-=1)
403 {
404 if (attr.equalsIgnoreCase((String)array[i]))
405 {
406 return true;
407 }
408 }
409 }
410 return false;
411 }
412
413 private static interface TagDecoratorExecutor
414 {
415 public Tag decorate(Tag orig, TagAttributes attributes);
416
417 }
418
419 private static abstract class TagSelector
420 {
421 public abstract TagDecoratorExecutor getExecutorIfApplies(Tag tag);
422 }
423
424 private static class TagSelectorImpl extends TagSelector implements TagDecoratorExecutor
425 {
426
427 private String attributeQName;
428 private String attributeLocalName;
429 private String attributePrefix;
430 private final String attributeNamespace;
431 private final String attributeAliasNamespace;
432 private String matchValue;
433
434 private String targetQName;
435 private String targetNamespace;
436 private String targetLocalName;
437
438 public TagSelectorImpl(String selector, String targetQName)
439 {
440
441
442
443 if (selector != null)
444 {
445 int i = selector.indexOf('=');
446 if (i >= 0)
447 {
448 this.attributeQName = selector.substring(0,i);
449 String value = selector.substring(i+1);
450 int s = value.indexOf('"');
451 int t = value.lastIndexOf('"');
452 if (s >= 0 && t >= 0 && t > s)
453 {
454 this.matchValue = value.substring(s+1,t);
455 }
456 else
457 {
458 this.matchValue = value;
459 }
460 }
461 else
462 {
463 this.attributeQName = selector;
464 this.matchValue = null;
465 }
466
467 int j = attributeQName.indexOf(':');
468 this.attributeLocalName = (j >= 0) ? attributeQName.substring(j+1) : attributeQName;
469 this.attributePrefix = (j >= 0) ? attributeQName.substring(0, j) : null;
470 this.attributeNamespace = resolveSelectorNamespace(this.attributePrefix);
471 this.attributeAliasNamespace = resolveAliasSelectorNamespace(this.attributePrefix);
472 }
473 else
474 {
475 this.attributeQName = null;
476 this.matchValue = null;
477 this.attributeLocalName = null;
478 this.attributePrefix = null;
479 this.attributeNamespace = "";
480 this.attributeAliasNamespace = null;
481 }
482
483 this.targetQName = targetQName;
484 if (targetQName != null)
485 {
486 int j = targetQName.indexOf(':');
487 if (j >= 0)
488 {
489
490 if (j == 1 && targetQName.charAt(0) == 'h')
491 {
492 this.targetNamespace = HtmlLibrary.NAMESPACE;
493 this.targetLocalName = targetQName.substring(j+1);
494 }
495 else if (j == 3 && targetQName.startsWith("jsf"))
496 {
497 this.targetNamespace = JsfLibrary.ALIAS_NAMESPACE;
498 this.targetLocalName = targetQName.substring(j+1);
499 }
500 }
501 else
502 {
503 this.targetLocalName = targetQName;
504 }
505 }
506 }
507
508 @Override
509 public TagDecoratorExecutor getExecutorIfApplies(Tag tag)
510 {
511 if (attributeQName != null)
512 {
513 if (matchValue != null)
514 {
515 String attributeNS = attributeNamespace;
516 TagAttribute attr = tag.getAttributes().get(attributeNS, attributeLocalName);
517 if (attr == null && attributeAliasNamespace.length() > 0)
518 {
519 attributeNS = attributeAliasNamespace;
520 attr = tag.getAttributes().get(attributeAliasNamespace, attributeLocalName);
521 }
522 if (attr != null)
523 {
524 if (attributeNS.equals(attr.getNamespace()) )
525 {
526
527 if (matchValue.equals(attr.getValue()))
528 {
529 return this;
530 }
531 else if ("*".equals(matchValue) && attr.getValue() != null)
532 {
533 return this;
534 }
535 }
536 else if (attributeNS == "" && attr.getNamespace() == null)
537 {
538
539 if (matchValue.equals(attr.getValue()))
540 {
541 return this;
542 }
543 else if ("*".equals(matchValue) && attr.getValue() != null)
544 {
545 return this;
546 }
547 }
548 }
549 }
550 else
551 {
552 String attributeNS = attributeNamespace;
553 TagAttribute attr = tag.getAttributes().get(attributeNS, attributeLocalName);
554 if (attr == null)
555 {
556 attributeNS = attributeAliasNamespace;
557 attr = tag.getAttributes().get(attributeNS, attributeLocalName);
558 }
559 if (attr != null)
560 {
561 if (attributeNS.equals(attr.getNamespace()))
562 {
563
564 return this;
565 }
566 else if (attributeNS == "" && attr.getNamespace() == null)
567 {
568
569 return this;
570 }
571 }
572 }
573 return null;
574 }
575 else
576 {
577 return this;
578 }
579 }
580
581 @Override
582 public Tag decorate(Tag orig, TagAttributes attributes)
583 {
584 return new Tag(orig.getLocation(), this.targetNamespace,
585 this.targetLocalName, this.targetQName, attributes);
586 }
587 }
588
589 private static String resolveSelectorNamespace(String prefix)
590 {
591 if ("jsf".equals(prefix))
592 {
593 return JsfLibrary.NAMESPACE;
594 }
595 else if ("h".equals(prefix))
596 {
597 return HtmlLibrary.NAMESPACE;
598 }
599 else if ("f".equals(prefix))
600 {
601 return CoreLibrary.NAMESPACE;
602 }
603 return "";
604 }
605
606 private static String resolveAliasSelectorNamespace(String prefix)
607 {
608 if ("jsf".equals(prefix))
609 {
610 return JsfLibrary.ALIAS_NAMESPACE;
611 }
612 else if ("h".equals(prefix))
613 {
614 return HtmlLibrary.ALIAS_NAMESPACE;
615 }
616 else if ("f".equals(prefix))
617 {
618 return CoreLibrary.ALIAS_NAMESPACE;
619 }
620 return "";
621 }
622 }