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  
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          // already available
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                      //Ignore
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                     // It will be resolved in the client, just add the expression.
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                 //Ignore
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                 //Ignore
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                 //Ignore
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         // Command pattern to apply the keyword or command to the base and then invoke the callback
382         FacesContext facesContext = searchExpressionContext.getFacesContext();
383 
384         SearchExpressionHandler handler = facesContext.getApplication().getSearchExpressionHandler();
385         
386         //Step 1: find base
387         //  Case ':' (root)
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         //Step 2: Once you have a base where you can start, apply an expression
397         if (topExpression.charAt(0) == KEYWORD_PREFIX.charAt(0))
398         {
399             // A keyword means apply a command over the current source using an expression and the result must be
400             // feedback into the algorithm.
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             // If the keyword is @child, @composite, @form, @namingcontainer, @next, @none, @parent, @previous,
410             // @root, @this ,  all commands change the source to be applied the action
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                 // Command completed, apply parent callback
430                 this.applyKeyword(searchExpressionContext, previous, command, null, parentCallback);
431             }
432             
433         }
434         else
435         {
436 
437             //Split expression into tokens and apply loop
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             // Use findComponent(...) passing the expression provided
452             UIComponent target = previous.findComponent(expression);
453             if (target == null)
454             {
455                 // If no component is found ...
456                 // First try to find the base component.
457 
458                 // Extract the base id from the expression string
459                 int idx = expression.indexOf(separatorChar);
460                 String base = idx > 0 ? expression.substring(0, idx) : expression;
461 
462                 // From the context component clientId, check if the base is part of the clientId
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                     // If there is a match, try to find a the first parent component whose id is equals to
471                     // the base id
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                     // if a base component is found ...
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                             // If no component is found,
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             // At this point if the algorithm hasn't returned and the topExpression does not have any separator char
536             // we need to do the search backward using findComponent.
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     // take the command and resolve it using the chain of responsibility pattern.
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         //Step 1: find base
609         //  Case ':' (root)
610         char separatorChar = facesContext.getNamingContainerSeparatorChar();
611         if (topExpression.charAt(0) == separatorChar)
612         {
613             // only keywords are passthrough expressions.
614             return false;
615         }
616 
617         //Step 2: Once you have a base where you can start, apply an expression
618         if (topExpression.charAt(0) == KEYWORD_PREFIX.charAt(0))
619         {
620             // A keyword means apply a command over the current source using an expression and the result must be
621             // feedback into the algorithm.
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             // If the keyword is @child, @composite, @form, @namingcontainer, @next, @none, @parent, @previous,
632             // @root, @this ,  all commands change the source to be applied the action
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             // Only keywords are valid to be passthrough. If it contains a chain of ids, this can only be resolved
649             // server side, because the tree structure and related clientId logic is only available server side.
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         // Command pattern to apply the keyword or command to the base and then invoke the callback
666         boolean isValid = true;
667         //Step 1: find base
668         //  Case ':' (root)
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         //Step 2: Once you have a base where you can start, apply an expression
677         if (topExpression.charAt(0) == KEYWORD_PREFIX.charAt(0))
678         {
679             // A keyword means apply a command over the current source using an expression and the result must be
680             // feedback into the algorithm.
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             // If the keyword is @child, @composite, @form, @namingcontainer, @next, @none, @parent, @previous,
691             // @root, @this ,  all commands change the source to be applied the action
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             //Split expression into tokens and apply loop
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             //Check expression
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                     //continue
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                 //Close first parentheses
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         // split expressions by blank or comma (and ignore blank and commas inside brackets)
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                     // lets add token inside buffer to our tokens
835                     String bufferString = buffer.toString().trim();
836                     if (bufferString.length() > 0)
837                     {
838                         tokens.add(bufferString);
839                     }
840                     // now we need to clear buffer
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         // lets not forget about part after the separator
855         tokens.add(buffer.toString());
856 
857         return tokens.toArray(new String[tokens.size()]);
858     }
859 
860 }