1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.myfaces.component.search;
21
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.List;
25 import javax.faces.FacesException;
26 import javax.faces.component.ContextCallback;
27 import javax.faces.component.NamingContainer;
28 import javax.faces.component.UIComponent;
29 import javax.faces.component.UIViewRoot;
30 import javax.faces.component.search.ComponentNotFoundException;
31 import javax.faces.component.search.SearchExpressionContext;
32 import javax.faces.component.search.SearchExpressionHandler;
33 import javax.faces.component.search.SearchExpressionHint;
34 import javax.faces.component.search.SearchKeywordContext;
35 import javax.faces.context.FacesContext;
36 import org.apache.myfaces.shared.renderkit.html.util.SharedStringBuilder;
37
38
39
40
41 public class SearchExpressionHandlerImpl extends SearchExpressionHandler
42 {
43 private static final String SB_SPLIT = SearchExpressionHandlerImpl.class.getName() + "#split";
44
45 protected void addHint(SearchExpressionContext searchExpressionContext, SearchExpressionHint hint)
46 {
47
48 if (!searchExpressionContext.getExpressionHints().contains(hint))
49 {
50 searchExpressionContext.getExpressionHints().add(hint);
51 }
52 }
53
54 protected boolean isHintSet(SearchExpressionContext searchExpressionContext, SearchExpressionHint hint)
55 {
56 if (searchExpressionContext.getExpressionHints() == null)
57 {
58 return false;
59 }
60
61 return searchExpressionContext.getExpressionHints().contains(hint);
62 }
63
64 @Override
65 public String resolveClientId(SearchExpressionContext searchExpressionContext, String expression)
66 {
67 if (expression == null)
68 {
69 expression = "";
70 }
71 else
72 {
73 expression = expression.trim();
74 }
75
76 FacesContext facesContext = searchExpressionContext.getFacesContext();
77 SearchExpressionHandler handler = facesContext.getApplication().getSearchExpressionHandler();
78
79 addHint(searchExpressionContext, SearchExpressionHint.RESOLVE_SINGLE_COMPONENT);
80
81 if (handler.isPassthroughExpression(searchExpressionContext, expression))
82 {
83 return expression;
84 }
85 else
86 {
87 CollectClientIdCallback callback = new CollectClientIdCallback();
88
89 if (!expression.isEmpty())
90 {
91 handler.invokeOnComponent(
92 searchExpressionContext, searchExpressionContext.getSource(), expression, callback);
93 }
94
95 if (!callback.isClientIdFound())
96 {
97 if (isHintSet(searchExpressionContext, SearchExpressionHint.IGNORE_NO_RESULT))
98 {
99
100 }
101 else
102 {
103 throw new ComponentNotFoundException("Cannot find component for expression \""
104 + expression + "\" referenced from \""
105 + searchExpressionContext.getSource().getClientId(facesContext) + "\".");
106 }
107 }
108 return callback.getClientId();
109 }
110 }
111
112 private static class CollectClientIdCallback implements ContextCallback
113 {
114 private String clientId = null;
115
116 @Override
117 public void invokeContextCallback(FacesContext context, UIComponent target)
118 {
119 if (clientId == null)
120 {
121 clientId = target.getClientId(context);
122 }
123 }
124
125 private String getClientId()
126 {
127 return clientId;
128 }
129
130 private boolean isClientIdFound()
131 {
132 return clientId != null;
133 }
134 }
135
136 @Override
137 public List<String> resolveClientIds(SearchExpressionContext searchExpressionContext, String expressions)
138 {
139 if (expressions == null)
140 {
141 expressions = "";
142 }
143 else
144 {
145 expressions = expressions.trim();
146 }
147
148 FacesContext facesContext = searchExpressionContext.getFacesContext();
149 SearchExpressionHandler handler = facesContext.getApplication().getSearchExpressionHandler();
150
151 CollectClientIdsCallback callback = new CollectClientIdsCallback();
152
153 if (!expressions.isEmpty())
154 {
155 for (String expression : handler.splitExpressions(facesContext, expressions))
156 {
157 if (handler.isPassthroughExpression(searchExpressionContext, expression))
158 {
159
160 callback.addClientId(expression);
161 }
162 else
163 {
164 handler.invokeOnComponent(
165 searchExpressionContext, searchExpressionContext.getSource(), expression, callback);
166 }
167 }
168 }
169
170 if (!callback.isClientIdFound())
171 {
172 if (isHintSet(searchExpressionContext, SearchExpressionHint.IGNORE_NO_RESULT))
173 {
174
175 }
176 else
177 {
178 throw new ComponentNotFoundException("Cannot find component for expression \""
179 + expressions + "\" referenced from \""
180 + searchExpressionContext.getSource().getClientId(facesContext) + "\".");
181 }
182 }
183 return callback.getClientIds();
184 }
185
186 private static class CollectClientIdsCallback implements ContextCallback
187 {
188 private List<String> clientIds = null;
189
190 @Override
191 public void invokeContextCallback(FacesContext context, UIComponent target)
192 {
193 if (clientIds == null)
194 {
195 clientIds = new ArrayList<String>();
196 }
197 clientIds.add(target.getClientId(context));
198 }
199
200 private List<String> getClientIds()
201 {
202 return clientIds == null ? Collections.emptyList() : clientIds;
203 }
204
205 private boolean isClientIdFound()
206 {
207 return clientIds != null;
208 }
209
210 private void addClientId(String clientId)
211 {
212 if (clientIds == null)
213 {
214 clientIds = new ArrayList<String>();
215 }
216 clientIds.add(clientId);
217 }
218 }
219
220 @Override
221 public void resolveComponent(SearchExpressionContext searchExpressionContext, String expression,
222 ContextCallback callback)
223 {
224 if (expression == null)
225 {
226 expression = "";
227 }
228 else
229 {
230 expression = expression.trim();
231 }
232
233 FacesContext facesContext = searchExpressionContext.getFacesContext();
234 SearchExpressionHandler handler = facesContext.getApplication().getSearchExpressionHandler();
235
236 SingleInvocationCallback checkCallback = new SingleInvocationCallback(callback);
237
238 addHint(searchExpressionContext, SearchExpressionHint.RESOLVE_SINGLE_COMPONENT);
239
240 if (!expression.isEmpty())
241 {
242 handler.invokeOnComponent(searchExpressionContext, searchExpressionContext.getSource(),
243 expression, checkCallback);
244 }
245
246 if (!checkCallback.isInvoked())
247 {
248 if (isHintSet(searchExpressionContext, SearchExpressionHint.IGNORE_NO_RESULT))
249 {
250
251 }
252 else
253 {
254 throw new ComponentNotFoundException("Cannot find component for expression \""
255 + expression + "\" referenced from \""
256 + searchExpressionContext.getSource().getClientId(facesContext) + "\".");
257 }
258 }
259 }
260
261 private static class SingleInvocationCallback implements ContextCallback
262 {
263 private boolean invoked;
264
265 private final ContextCallback innerCallback;
266
267 public SingleInvocationCallback(ContextCallback innerCallback)
268 {
269 this.innerCallback = innerCallback;
270 this.invoked = false;
271 }
272
273 @Override
274 public void invokeContextCallback(FacesContext context, UIComponent target)
275 {
276 if (!isInvoked())
277 {
278 try
279 {
280 innerCallback.invokeContextCallback(context, target);
281 }
282 finally
283 {
284 invoked = true;
285 }
286 }
287 }
288
289 public boolean isInvoked()
290 {
291 return invoked;
292 }
293 }
294
295 @Override
296 public void resolveComponents(SearchExpressionContext searchExpressionContext, String expressions,
297 ContextCallback callback)
298 {
299 if (expressions == null)
300 {
301 expressions = "";
302 }
303 else
304 {
305 expressions = expressions.trim();
306 }
307
308 FacesContext facesContext = searchExpressionContext.getFacesContext();
309 SearchExpressionHandler handler = facesContext.getApplication().getSearchExpressionHandler();
310
311 MultipleInvocationCallback checkCallback = new MultipleInvocationCallback(callback);
312
313 if (!expressions.isEmpty())
314 {
315 for (String expression : handler.splitExpressions(facesContext, expressions))
316 {
317 handler.invokeOnComponent(searchExpressionContext, searchExpressionContext.getSource(),
318 expression, checkCallback);
319 }
320 }
321
322 if (!checkCallback.isInvoked())
323 {
324 if (isHintSet(searchExpressionContext, SearchExpressionHint.IGNORE_NO_RESULT))
325 {
326
327 }
328 else
329 {
330 throw new ComponentNotFoundException("Cannot find component for expression \""
331 + expressions + "\" referenced from \""
332 + searchExpressionContext.getSource().getClientId(facesContext) + "\".");
333 }
334 }
335 }
336
337 private static class MultipleInvocationCallback implements ContextCallback
338 {
339 private boolean invoked;
340
341 private final ContextCallback innerCallback;
342
343 public MultipleInvocationCallback(ContextCallback innerCallback)
344 {
345 this.innerCallback = innerCallback;
346 this.invoked = false;
347 }
348
349 @Override
350 public void invokeContextCallback(FacesContext context, UIComponent target)
351 {
352 try
353 {
354 innerCallback.invokeContextCallback(context, target);
355 }
356 finally
357 {
358 invoked = true;
359 }
360 }
361
362 public boolean isInvoked()
363 {
364 return invoked;
365 }
366 }
367
368 @Override
369 public void invokeOnComponent(final SearchExpressionContext searchExpressionContext,
370 UIComponent previous, String topExpression, ContextCallback topCallback)
371 {
372 if (topExpression == null)
373 {
374 topExpression = "";
375 }
376 else
377 {
378 topExpression = topExpression.trim();
379 }
380
381
382 FacesContext facesContext = searchExpressionContext.getFacesContext();
383
384 SearchExpressionHandler handler = facesContext.getApplication().getSearchExpressionHandler();
385
386
387
388 char separatorChar = facesContext.getNamingContainerSeparatorChar();
389 if (topExpression.charAt(0) == separatorChar)
390 {
391 UIComponent findBase = SearchComponentUtils.getRootComponent(previous);
392 handler.invokeOnComponent(searchExpressionContext, findBase, topExpression.substring(1), topCallback);
393 return;
394 }
395
396
397 if (topExpression.charAt(0) == KEYWORD_PREFIX.charAt(0))
398 {
399
400
401
402 String command = extractKeyword(topExpression, 1, separatorChar);
403 final String remaining =
404 command.length()+1 < topExpression.length() ?
405 topExpression.substring(1+command.length()+1) : null;
406
407 final ContextCallback parentCallback = topCallback;
408
409
410
411 if (remaining != null)
412 {
413 if (facesContext.getApplication().getSearchKeywordResolver().isLeaf(searchExpressionContext, command))
414 {
415 throw new FacesException("Expression cannot have keywords or ids at the right side: "+command);
416 }
417 this.applyKeyword(searchExpressionContext, previous, command, remaining, new ContextCallback()
418 {
419 @Override
420 public void invokeContextCallback(FacesContext facesContext, UIComponent target)
421 {
422 handler.invokeOnComponent(
423 searchExpressionContext, target, remaining, parentCallback);
424 }
425 });
426 }
427 else
428 {
429
430 this.applyKeyword(searchExpressionContext, previous, command, null, parentCallback);
431 }
432
433 }
434 else
435 {
436
437
438 String nextExpression = null;
439 String expression;
440 if (topExpression.indexOf(":@") > 0)
441 {
442 int idx = topExpression.indexOf(":@");
443 nextExpression = topExpression.substring(idx+1);
444 expression = topExpression.substring(0, idx);
445 }
446 else
447 {
448 expression = topExpression;
449 }
450
451
452 UIComponent target = previous.findComponent(expression);
453 if (target == null)
454 {
455
456
457
458
459 int idx = expression.indexOf(separatorChar);
460 String base = idx > 0 ? expression.substring(0, idx) : expression;
461
462
463 String contextClientId = previous.getClientId(facesContext);
464 int startCommon = contextClientId.lastIndexOf(base+facesContext.getNamingContainerSeparatorChar());
465 if (startCommon >= 0
466 && (startCommon == 0 || contextClientId.charAt(startCommon-1) == separatorChar )
467 && (startCommon+base.length() <= contextClientId.length()-1 ||
468 contextClientId.charAt(startCommon+base.length()+1) == separatorChar ))
469 {
470
471
472 UIComponent parent = previous;
473 while (parent != null )
474 {
475 if (base.equals(parent.getId()) && parent instanceof NamingContainer)
476 {
477 break;
478 }
479 else
480 {
481 parent = parent.getParent();
482 }
483 }
484
485
486 if (parent != null)
487 {
488 target = parent.findComponent(expression);
489 if (target == null && !searchExpressionContext.getExpressionHints().contains(
490 SearchExpressionHint.SKIP_VIRTUAL_COMPONENTS))
491 {
492 contextClientId = parent.getClientId(facesContext);
493
494 String targetClientId = contextClientId.substring(0, startCommon+base.length()) +
495 expression.substring(base.length());
496
497 if (nextExpression != null)
498 {
499 final String childExpression = nextExpression;
500
501 parent.invokeOnComponent(facesContext, targetClientId, new ContextCallback()
502 {
503 @Override
504 public void invokeContextCallback(FacesContext context, UIComponent target)
505 {
506 handler.invokeOnComponent(
507 searchExpressionContext, target, childExpression, topCallback);
508 }
509 });
510 }
511 else
512 {
513 parent.invokeOnComponent(facesContext, targetClientId, topCallback);
514 }
515 return;
516 }
517 }
518 }
519 }
520
521 if (target != null)
522 {
523 if (nextExpression != null)
524 {
525 handler.invokeOnComponent(searchExpressionContext, target, nextExpression, topCallback);
526 }
527 else
528 {
529 topCallback.invokeContextCallback(facesContext, target);
530 }
531
532 return;
533 }
534
535
536
537 if (target == null
538 && searchExpressionContext.getSource() == previous
539 && (topExpression.indexOf(separatorChar) == -1) )
540 {
541 UIComponent baseNC = previous.getNamingContainer();
542 if (baseNC != null && baseNC.getParent() != null)
543 {
544 UIComponent parentNC = getParentNamingContainerUIViewRoot(baseNC.getParent());
545 while (target == null && parentNC != null)
546 {
547 UIComponent parent = parentNC.getParent();
548 target = parentNC.findComponent(expression);
549 if (parent != null)
550 {
551 parentNC = getParentNamingContainerUIViewRoot(parent);
552 }
553 else
554 {
555 parentNC = null;
556 }
557 }
558 if (target != null)
559 {
560 topCallback.invokeContextCallback(facesContext, target);
561 return;
562 }
563 }
564 }
565
566 topCallback.invokeContextCallback(facesContext, previous);
567 }
568
569 }
570
571 private static UIComponent getParentNamingContainerUIViewRoot(UIComponent component)
572 {
573 do
574 {
575 if (component instanceof NamingContainer || component instanceof UIViewRoot)
576 {
577 return component;
578 }
579
580 component = component.getParent();
581 } while (component != null);
582 return null;
583 }
584
585
586 protected void applyKeyword(SearchExpressionContext searchExpressionContext, UIComponent last,
587 String command, String remainingExpression, ContextCallback topCallback)
588 {
589 SearchKeywordContext searchContext =
590 new SearchKeywordContext(searchExpressionContext, topCallback, remainingExpression);
591
592 searchExpressionContext.getFacesContext().getApplication()
593 .getSearchKeywordResolver().resolve(searchContext, last, command);
594 }
595
596 @Override
597 public boolean isPassthroughExpression(SearchExpressionContext searchExpressionContext, String topExpression)
598 {
599 if (topExpression == null || topExpression.trim().isEmpty())
600 {
601 return false;
602 }
603
604 topExpression = topExpression.trim();
605
606 FacesContext facesContext = searchExpressionContext.getFacesContext();
607
608
609
610 char separatorChar = facesContext.getNamingContainerSeparatorChar();
611 if (topExpression.charAt(0) == separatorChar)
612 {
613
614 return false;
615 }
616
617
618 if (topExpression.charAt(0) == KEYWORD_PREFIX.charAt(0))
619 {
620
621
622
623 String command = extractKeyword(topExpression, 1, separatorChar);
624 final String remaining =
625 command.length()+1 < topExpression.length() ?
626 topExpression.substring(1+command.length()+1) : null;
627
628 final SearchExpressionHandler currentInstance =
629 facesContext.getApplication().getSearchExpressionHandler();
630
631
632
633 boolean passthrough = facesContext.getApplication().getSearchKeywordResolver().isPassthrough(
634 searchExpressionContext, command);
635
636 if (passthrough)
637 {
638 return remaining != null ?
639 currentInstance.isPassthroughExpression(searchExpressionContext, remaining) : true;
640 }
641 else
642 {
643 return false;
644 }
645 }
646 else
647 {
648
649
650 return false;
651 }
652 }
653
654 @Override
655 public boolean isValidExpression(SearchExpressionContext searchExpressionContext, String topExpression)
656 {
657 if (topExpression == null || topExpression.trim().isEmpty())
658 {
659 return true;
660 }
661
662 topExpression = topExpression.trim();
663
664 FacesContext facesContext = searchExpressionContext.getFacesContext();
665
666 boolean isValid = true;
667
668
669 char separatorChar = facesContext.getNamingContainerSeparatorChar();
670 if (topExpression.charAt(0) == separatorChar)
671 {
672 return facesContext.getApplication().getSearchExpressionHandler().isValidExpression(
673 searchExpressionContext, topExpression.substring(1));
674 }
675
676
677 if (topExpression.charAt(0) == KEYWORD_PREFIX.charAt(0))
678 {
679
680
681
682 String command = extractKeyword(topExpression, 1, separatorChar);
683 final String remaining =
684 command.length()+1 < topExpression.length() ?
685 topExpression.substring(1+command.length()+1) : null;
686
687 final SearchExpressionHandler currentInstance =
688 facesContext.getApplication().getSearchExpressionHandler();
689
690
691
692 isValid = facesContext.getApplication().getSearchKeywordResolver().isResolverForKeyword(
693 searchExpressionContext, command);
694 if (remaining != null)
695 {
696 if (facesContext.getApplication().getSearchKeywordResolver().isLeaf(
697 searchExpressionContext, command))
698 {
699 isValid = false;
700 }
701 return !isValid ? false : currentInstance.isValidExpression(searchExpressionContext, remaining);
702 }
703 }
704 else
705 {
706
707 String nextExpression = null;
708 String expression = null;
709 if (topExpression.indexOf(":@") > 0)
710 {
711 int idx = topExpression.indexOf(":@");
712 nextExpression = topExpression.substring(idx+1);
713 expression = topExpression.substring(0,idx);
714 }
715 else
716 {
717 expression = topExpression;
718 }
719
720
721 for (int i = 0; i < expression.length(); i++)
722 {
723 char c = expression.charAt(i);
724 if (Character.isLetterOrDigit(c) || c == '-' || c == '_' || c == separatorChar)
725 {
726
727 }
728 else
729 {
730 isValid = false;
731 }
732 }
733
734 if (nextExpression != null)
735 {
736 return !isValid ? false : facesContext.getApplication().getSearchExpressionHandler()
737 .isValidExpression(searchExpressionContext, nextExpression);
738 }
739 }
740 return isValid;
741 }
742
743 private static String extractKeyword(String expression, int startIndex, char separatorChar)
744 {
745 int parenthesesCounter = -1;
746 int count = -1;
747 for (int i = startIndex; i < expression.length(); i++)
748 {
749 char c = expression.charAt(i);
750 if (c == '(')
751 {
752 if (parenthesesCounter == -1)
753 {
754 parenthesesCounter = 0;
755 }
756 parenthesesCounter++;
757 }
758 if (c == ')')
759 {
760 parenthesesCounter--;
761 }
762 if (parenthesesCounter == 0)
763 {
764
765 count = i+1;
766 break;
767 }
768 if (parenthesesCounter == -1)
769 {
770 if (c == separatorChar)
771 {
772 count = i;
773 break;
774 }
775 }
776 }
777 if (count == -1)
778 {
779 return expression.substring(startIndex);
780 }
781 else
782 {
783 return expression.substring(startIndex, count);
784 }
785 }
786
787 @Override
788 public String[] splitExpressions(FacesContext context, String expressions)
789 {
790
791 String[] splittedExpressions = split(context, expressions, EXPRESSION_SEPARATOR_CHARS);
792 return splittedExpressions;
793 }
794
795 private static String[] split(FacesContext context, String value, char... separators)
796 {
797 if (value == null)
798 {
799 return null;
800 }
801
802 List<String> tokens = new ArrayList<String>();
803 StringBuilder buffer = SharedStringBuilder.get(context, SB_SPLIT);
804
805 int parenthesesCounter = 0;
806
807 char[] charArray = value.toCharArray();
808
809 for (char c : charArray)
810 {
811 if (c == '(')
812 {
813 parenthesesCounter++;
814 }
815
816 if (c == ')')
817 {
818 parenthesesCounter--;
819 }
820
821 if (parenthesesCounter == 0)
822 {
823 boolean isSeparator = false;
824 for (char separator : separators)
825 {
826 if (c == separator)
827 {
828 isSeparator = true;
829 }
830 }
831
832 if (isSeparator)
833 {
834
835 String bufferString = buffer.toString().trim();
836 if (bufferString.length() > 0)
837 {
838 tokens.add(bufferString);
839 }
840
841 buffer.delete(0, buffer.length());
842 }
843 else
844 {
845 buffer.append(c);
846 }
847 }
848 else
849 {
850 buffer.append(c);
851 }
852 }
853
854
855 tokens.add(buffer.toString());
856
857 return tokens.toArray(new String[tokens.size()]);
858 }
859
860 }