Organized imports
[phpeclipse.git] / archive / net.sourceforge.phpeclipse.js.core / src / net / sourceforge / phpeclipse / js / core / parser / JSParser.java
1 /*
2  * $RCSfile: JSParser.java,v $
3  *
4  * Copyright 2002
5  * CH-1700 Fribourg, Switzerland
6  * All rights reserved.
7  *
8  *========================================================================
9  * Modifications history
10  *========================================================================
11  * $Log: not supported by cvs2svn $
12  * Revision 1.2  2005/04/06 18:29:29  axelcl
13  * Avoid NullPointerException
14  *
15  * Revision 1.1  2004/09/02 18:14:38  jsurfer
16  * intial source from ttp://www.sf.net/projects/wdte
17  *
18  * Revision 1.1  2004/02/26 02:25:42  agfitzp
19  * renamed packages to match xml & css
20  *
21  * Revision 1.1  2004/02/05 03:10:08  agfitzp
22  * Initial Submission
23  *
24  * Revision 1.1.2.1  2003/12/12 21:37:24  agfitzp
25  * Experimental work for Classes view
26  *
27  * Revision 1.6  2003/12/10 20:19:16  agfitzp
28  * 3.0 port
29  *
30  * Revision 1.5  2003/06/21 03:48:51  agfitzp
31  * fixed global variables as functions bug
32  * fixed length calculation of instance variables
33  * Automatic outlining is now a preference
34  *
35  * Revision 1.4  2003/05/30 20:53:09  agfitzp
36  * 0.0.2 : Outlining is now done as the user types. Some other bug fixes.
37  *
38  * Revision 1.3  2003/05/28 20:47:58  agfitzp
39  * Outline the document, not the file.
40  *
41  * Revision 1.2  2003/05/28 15:20:00  agfitzp
42  * Trivial change to test CVS commit
43  *
44  * Revision 1.1  2003/05/28 15:17:12  agfitzp
45  * net.sourceforge.phpeclipse.js.core 0.0.1 code base
46  *
47  *========================================================================
48 */
49
50 package net.sourceforge.phpeclipse.js.core.parser;
51
52 import java.io.ByteArrayOutputStream;
53 import java.io.IOException;
54 import java.io.InputStream;
55 import java.util.HashMap;
56 import java.util.LinkedList;
57 import java.util.List;
58
59 import net.sourceforge.phpeclipse.js.core.model.JSClassElement;
60 import net.sourceforge.phpeclipse.js.core.model.JSClassMethodElement;
61 import net.sourceforge.phpeclipse.js.core.model.JSClassVariableElement;
62 import net.sourceforge.phpeclipse.js.core.model.JSElement;
63 import net.sourceforge.phpeclipse.js.core.model.JSFunctionElement;
64 import net.sourceforge.phpeclipse.js.core.model.JSGlobalVariableElement;
65 import net.sourceforge.phpeclipse.js.core.model.JSInstanceMethodElement;
66 import net.sourceforge.phpeclipse.js.core.model.JSInstanceVariableElement;
67
68 import org.eclipse.core.resources.IFile;
69 import org.eclipse.jface.text.BadLocationException;
70 import org.eclipse.jface.text.Document;
71 import org.eclipse.jface.text.IDocument;
72 import org.eclipse.jface.text.rules.IToken;
73
74 /**
75  * DOCUMENT ME!
76  * 
77  * @author Addi 
78  */
79 public class JSParser
80 {
81
82         public static final String FUNCTION = "function";
83
84         /**
85          * line separator
86          */
87         public static final String LINE_SEPARATOR = System.getProperty("line.separator");
88
89         /**
90          * Array of system types to ignore.
91          */
92         private static String[] systemClassNames= {"Array","String"};
93
94
95         protected HashMap systemClassMap = new HashMap();
96           
97         protected IFile sourceFile;
98         protected IDocument sourceDocument;
99         protected HashMap functions = new HashMap();
100         protected HashMap classes = new HashMap();
101         protected HashMap globalVariables = new HashMap();
102         protected List elementList = new LinkedList();
103         protected JSSyntaxScanner scanner = new JSSyntaxScanner();
104
105         /**
106          * Constructor for JSParser.
107          */
108         public JSParser()
109         {
110                 super();
111
112                 int i;
113
114                 for(i = 0;i < systemClassNames.length; i++)
115                 {
116                         String aName = systemClassNames[i];
117                         systemClassMap.put(aName, aName);                
118                 }
119         }
120
121         /**
122          * Returns a string containing the contents of the given file.  Returns an empty string if there
123          * were any errors reading the file.
124          * @param file
125          * 
126          * @return
127          */
128         protected static String getText(IFile file)
129         {
130                 try
131                 {
132                         InputStream in = file.getContents();
133                         return streamToString(in);
134                 } catch (Exception e)
135                 {
136                         e.printStackTrace();
137                 }
138                 return "";
139         }
140
141         protected static String streamToString(InputStream in) throws IOException
142         {
143                 ByteArrayOutputStream out = new ByteArrayOutputStream();
144                 byte[] buf = new byte[1024];
145                 int read = in.read(buf);
146
147                 while (read > 0)
148                 {
149                         out.write(buf, 0, read);
150                         read = in.read(buf);
151                 }
152
153                 return out.toString();
154         }
155
156         /**
157          * Skips ahead and finds next non-whitespace token.
158          *
159          */
160         public IToken nextNonWhitespaceToken()
161         {
162                 IToken aToken = scanner.nextToken();
163
164                 while (!aToken.isEOF() && aToken.isWhitespace())
165                 {
166                         aToken = scanner.nextToken();
167                 }
168
169                 return aToken;
170         }
171
172         /**
173          * Parses the input given by the argument.
174          * 
175          * @param file  the element containing the input text
176          * 
177          * @return an element collection representing the parsed input
178          */
179         public List parse(IFile file)
180         {
181                 this.sourceFile = file;
182                 return parse(new Document(getText(file)));
183         }
184
185         /**
186          * Parses the input given by the argument.
187          * 
188          * @param aSourceDocument  the element containing the input text
189          * 
190          * @return an element collection representing the parsed input
191          */
192         public List parse(IDocument aSourceDocument)
193         {
194                 sourceDocument = aSourceDocument;
195                 
196                 scanner.setRange(sourceDocument, 0, sourceDocument.getLength());
197                 IToken token = scanner.nextToken();
198                 while (!token.isEOF())
199                 {
200                         int offset = scanner.getTokenOffset();
201                         int length = scanner.getTokenLength();
202                         String expression = getExpression(offset, length);
203                 
204                         if (token.equals(JSSyntaxScanner.TOKEN_FUNCTION))
205                         {
206                                 addFunction(expression, offset, length);
207                         }
208                 
209                         if (token.equals(JSSyntaxScanner.TOKEN_DEFAULT))
210                         {
211                                 //We need to check if the token is already a function or class
212                                 if (functions.containsKey(expression) || classes.containsKey(expression))
213                                 {
214                                         token = nextNonWhitespaceToken();
215                                         if (token.equals(JSSyntaxScanner.TOKEN_MEMBER))
216                                         {
217                                                 detectInstanceMethod(offset, expression);
218                                         } else
219                                         {
220                                                 detectClassMethod(token, offset, expression);
221                                         }
222                                 } else
223                                 {
224                                         if (expression.equals("var"))
225                                         {
226                                                 detectGlobalVariable();
227                                         }
228                                 }
229                         }
230                         token = scanner.nextToken();
231                 }
232                 return elementList;
233         }
234
235         private void addFunction(String expression, int offset, int length)
236         {
237                 String functionSignature = getNaked(expression);
238                 int braceOffset = functionSignature.indexOf("(");
239                 String functionName = functionSignature.substring(0, braceOffset).trim();
240                 String arguments =
241                         functionSignature.substring(functionSignature.indexOf("("), functionSignature.indexOf(")") + 1);
242
243                 if (functionName.indexOf(".") >= 0)
244                 {
245                         //If the function signature includes .prototype. then it's a member.
246                         if (functionName.indexOf(".prototype.") >= 0)
247                         {
248                                 String className = functionName.substring(0, functionName.indexOf("."));
249                                 String memberName = functionName.substring(functionName.lastIndexOf(".") + 1);
250                                 JSInstanceMethodElement aMethod =
251                                         this.addInstanceMethod(memberName, className, arguments, offset, offset, length);
252                                 detectInstanceMethodContext(className, aMethod);
253                         } else
254                         {
255                                 String className = functionName.substring(0, functionName.indexOf("."));
256                                 if (functions.containsKey(className) || classes.containsKey(className))
257                                 {
258                                         String memberName = functionName.substring(functionName.lastIndexOf(".") + 1);
259                                         JSFunctionElement aMethod =
260                                                 this.addClassMethod(memberName, className, arguments, offset, offset, length);
261                                 }
262                         }
263                 } else
264                 {
265                         if(! functions.containsKey(functionName))
266                         {
267                                 JSFunctionElement aFunction = new JSFunctionElement(this.sourceFile, functionName, arguments, offset, length);
268         
269                                 elementList.add(aFunction);
270                                 functions.put(functionName, aFunction);
271         
272                                 detectFunctionContext(aFunction);
273                         }
274                 }
275         }
276
277         /**
278          *
279          */     
280         private void checkForSpecialGlobalTypes(JSGlobalVariableElement aVariable)
281         {
282                 IToken token = nextNonWhitespaceToken();
283                 if (!token.isEOF())
284                 {
285                         if(!checkForDynamicClass(aVariable, token))
286                         {
287                                 checkForAnonymousFunction(aVariable, token);
288                         }
289                 }
290         }
291
292         /**
293          *
294          */     
295         private boolean checkForDynamicClass(JSGlobalVariableElement aVariable, IToken rhsToken)
296         {
297                 if (rhsToken.equals(JSSyntaxScanner.TOKEN_DEFAULT))
298                 {
299                         int offset = scanner.getTokenOffset();
300                         int length = scanner.getTokenLength();
301                         
302                         String expression = getExpression(offset, length);
303
304                         if (expression.equals("new"))
305                         {
306                                 IToken token = nextNonWhitespaceToken();
307                                 if (!token.isEOF())
308                                 {
309                                         if (token.equals(JSSyntaxScanner.TOKEN_DEFAULT))
310                                         {
311                                                 offset = scanner.getTokenOffset();
312                                                 length = scanner.getTokenLength();
313                                                 expression = getExpression(offset, length);
314                                                                                                                                                 
315                                                 if(! isSystemClass(expression))
316                                                 {
317                                                         JSClassElement aClass = findOrCreateClass(aVariable.getName());
318                                                         if(aClass != null)
319                                                         {
320                                                                 //Tell the class it's dynamically declared: what we will parse as class methods & vars are really instance methods & vars
321                                                                 aClass.setPrototype(true);
322                                                                 
323                                                                 return true;
324                                                         }
325                                                 }
326                                         }
327                                 }
328                         }
329                 }
330                 return false;   
331         }
332
333         /**
334          *
335          */     
336         private boolean checkForAnonymousFunction(JSGlobalVariableElement aVariable, IToken rhsToken)
337         {
338                 if (rhsToken.equals(JSSyntaxScanner.TOKEN_FUNCTION))
339                 {
340                         String functionName = aVariable.getName();
341                         int offset = aVariable.getOffset();
342                         int length = aVariable.getLength();
343                         
344                         int functionOffset = scanner.getTokenOffset();
345                         int functionLength = scanner.getTokenLength();
346                         String functionSignature =
347                                 getExpression(functionOffset, functionLength);
348                         String arguments = getArgumentString(functionSignature);
349
350                         JSFunctionElement aFunction = new JSFunctionElement(this.sourceFile, functionName, arguments, offset, functionOffset - offset + functionLength);
351
352                         elementList.add(aFunction);
353                         functions.put(functionName, aFunction);
354
355                         elementList.remove(aVariable);
356                         globalVariables.remove(functionName);
357
358                         detectFunctionContext(aFunction);
359                         
360                         return true;
361                 }
362                 
363                 return false;
364         }               
365
366         /**
367          *
368          */     
369         private String getExpression(int offset, int length)
370         {
371                 String expression;
372                 try {
373                         expression = sourceDocument.get(offset, length);//sourceBuffer.substring(offset, offset + length);
374                 } catch(BadLocationException e)
375                 {
376                         expression = "";
377                 }
378                 return expression;
379         }
380
381         /**
382          *
383          */     
384         private void detectGlobalVariable()
385         {
386                 IToken token;
387                 int length;
388                 int offset;
389
390                 token = nextNonWhitespaceToken();
391                 if (!token.isEOF())
392                 {
393                         if (token.equals(JSSyntaxScanner.TOKEN_DEFAULT))
394                         {
395                                 int varOffset = scanner.getTokenOffset();
396                                 length = scanner.getTokenLength();
397                                 String variableName = getExpression(varOffset, length);
398
399                                 token = nextNonWhitespaceToken();
400                                 if (!token.isEOF())
401                                 {
402                                         offset = scanner.getTokenOffset();
403                                         length = scanner.getTokenLength();
404                                         String expression = getExpression(offset, length);
405                                         if (expression.equals("="))
406                                         {
407                                                 JSGlobalVariableElement aVariable = addGlobalVariable(variableName, varOffset);
408                                                 if (aVariable!=null) {
409                                                   checkForSpecialGlobalTypes(aVariable);
410                                                 }
411                                         }
412                                 }
413                         }
414                 }
415         }
416
417         private void detectClassMethod(IToken token, int classOffset, String className)
418         {
419                 int offset = scanner.getTokenOffset();
420                 int length = scanner.getTokenLength();
421                 String expression = getExpression(offset, length);
422                 
423                 if (expression.equals("."))
424                 {
425
426                         token = nextNonWhitespaceToken();
427                         if (!token.isEOF())
428                         {
429                                 offset = scanner.getTokenOffset();
430                                 length = scanner.getTokenLength();
431                                 String memberName = getExpression(offset, length);
432                 
433                                 token = nextNonWhitespaceToken();
434                                 if (!token.isEOF())
435                                 {
436                                         offset = scanner.getTokenOffset();
437                                         length = scanner.getTokenLength();
438                                         expression = getExpression(offset, length);
439                                         if (expression.equals("="))
440                                         {
441         
442                                                 token = nextNonWhitespaceToken();
443                                                 int tokenOffset = scanner.getTokenOffset();
444                                                 int tokenLength = scanner.getTokenLength();
445         
446                                                 if (token.equals(JSSyntaxScanner.TOKEN_FUNCTION))
447                                                 {
448                                                         String functionSignature = getExpression(tokenOffset, tokenLength);
449                                                         String arguments = getArgumentString(functionSignature);
450         
451                                                         JSFunctionElement aMethod =
452                                                                 addClassMethod(memberName, className, arguments, classOffset, tokenOffset, tokenLength);
453                                                         
454         
455                                                 } else
456                                                 {
457                                                         addClassVariable(memberName, className, classOffset);
458                                                 }
459                                         }
460                                 }
461                         }
462                 }
463         }
464
465         private String getArgumentString(String functionSignature)
466         {
467                 return functionSignature.substring(
468                                 functionSignature.indexOf("("),
469                                 functionSignature.indexOf(")") + 1);
470         }
471
472         private void detectInstanceMethod(int classOffset, String className)
473         {
474                 String expression;
475                 IToken token;
476                 int length;
477                 int offset;
478
479                 token = nextNonWhitespaceToken();
480                 if (!token.isEOF())
481                 {
482                         offset = scanner.getTokenOffset();
483                         length = scanner.getTokenLength();
484                         expression = getExpression(offset, length);
485
486                         if (expression.equals("."))
487                         {
488
489                                 token = nextNonWhitespaceToken();
490                                 if (!token.isEOF())
491                                 {
492                                         offset = scanner.getTokenOffset();
493                                         length = scanner.getTokenLength();
494                                         String memberName = getExpression(offset, length);
495
496                                         token = nextNonWhitespaceToken();
497                                         if (!token.isEOF())
498                                         {
499                                                 offset = scanner.getTokenOffset();
500                                                 length = scanner.getTokenLength();
501                                                 expression = getExpression(offset, length);
502                                                 if (expression.equals("="))
503                                                 {
504                                                         token = nextNonWhitespaceToken();
505                                                         if (token.equals(JSSyntaxScanner.TOKEN_FUNCTION))
506                                                         {
507                                                                 int functionOffset = scanner.getTokenOffset();
508                                                                 int functionLength = scanner.getTokenLength();
509                                                                 String functionSignature =
510                                                                         getExpression(functionOffset, functionLength);
511                                                                 String arguments = getArgumentString(functionSignature);
512         
513                                                                 JSInstanceMethodElement aMethod =
514                                                                         addInstanceMethod(
515                                                                                 memberName,
516                                                                                 className,
517                                                                                 arguments,
518                                                                                 classOffset,
519                                                                                 functionOffset,
520                                                                                 functionLength);
521         
522                                                                 detectInstanceMethodContext(className, aMethod);
523         
524                                                         } else
525                                                         {
526                                                                 addInstanceVariable(memberName, className, classOffset, (".prototype.").length());
527                                                         }
528         
529                                                 }
530                                         }
531                                 }
532                         }
533                 }
534         }
535
536         private void parseInstanceMethodContext(String className, JSFunctionElement aMethod)
537         {
538                 IToken token;
539
540                 token = nextNonWhitespaceToken();
541                 while (!token.isEOF())
542                 {
543                         int offset = scanner.getTokenOffset();
544                         int length = scanner.getTokenLength();
545                         String expression = getExpression(offset, length);
546
547                         //                      if (token.equals(JSSyntaxScanner.TOKEN_END_CONTEXT))
548                         if (expression.equals("}"))
549                         {
550                                 return;
551                         } else if (expression.equals("{"))
552                         {
553                                 parseInstanceMethodContext(className, aMethod);
554                         } else if (token.equals(JSSyntaxScanner.TOKEN_DEFAULT))
555                         {
556                                 if (expression.equals("this"))
557                                 {
558                                         handleThisReference(className, offset);
559                                 }
560                         }
561
562                         token = nextNonWhitespaceToken();
563                 }
564         }
565
566         private void detectInstanceMethodContext(String className, JSFunctionElement aMethod)
567         {
568                 IToken token;
569
570                 token = nextNonWhitespaceToken();
571                 while (!token.isEOF())
572                 {
573                         int offset = scanner.getTokenOffset();
574                         int length = scanner.getTokenLength();
575                         String expression = getExpression(offset, length);
576
577                         //                      if (token.equals(JSSyntaxScanner.TOKEN_BEGIN_CONTEXT))
578                         if (expression.equals("{"))
579                         {
580                                 parseInstanceMethodContext(className, aMethod);
581                                 return;
582                         }
583
584                         token = nextNonWhitespaceToken();
585                 }
586         }
587
588         private void parseClassMethodContext(JSFunctionElement aMethod)
589         {
590                 IToken token;
591
592                 token = nextNonWhitespaceToken();
593                 while (!token.isEOF())
594                 {
595                         int offset = scanner.getTokenOffset();
596                         int length = scanner.getTokenLength();
597                         String expression = getExpression(offset, length);
598
599                         if (expression.equals("}"))
600                         {
601                                 return;
602                         } else if (expression.equals("{"))
603                         {
604                                 parseClassMethodContext(aMethod);
605                         }
606
607                         token = nextNonWhitespaceToken();
608                 }
609         }
610
611         private void detectClassMethodContext(JSFunctionElement aMethod)
612         {
613                 IToken token = nextNonWhitespaceToken();
614                 while (!token.isEOF())
615                 {
616                         int offset = scanner.getTokenOffset();
617                         int length = scanner.getTokenLength();
618                         String expression = getExpression(offset, length);
619
620                         if (expression.equals("{"))
621                         {
622                                 parseClassMethodContext(aMethod);
623                                 return;
624                         }
625
626                         token = nextNonWhitespaceToken();
627                 }
628         }
629
630         private void handleThisReference(String className, int expressionStart)
631         {
632                 IToken token = nextNonWhitespaceToken();
633                 if (!token.isEOF())
634                 {
635                         int offset = scanner.getTokenOffset();
636                         int length = scanner.getTokenLength();
637         
638                         String expression = getExpression(offset, length);
639         
640                         if(expression.equals("."))
641                         {
642                                 token = nextNonWhitespaceToken();
643                                 if (!token.isEOF())
644                                 {
645                                         int memberStart = scanner.getTokenOffset();
646                                         length = scanner.getTokenLength();
647         
648                                         String memberName = getExpression(memberStart, length);
649         
650                                         token = nextNonWhitespaceToken();
651                                         if (!token.isEOF())
652                                         {
653                                                 offset = scanner.getTokenOffset();
654                                                 length = scanner.getTokenLength();
655                                                 expression = getExpression(offset, length);
656                 
657                                                 if (expression.equals("="))
658                                                 {
659                                                         addInstanceVariable(memberName, className, expressionStart, 1 + 4 - className.length());
660                                                 }
661                                         }
662                                 }
663                         }
664                 }
665         }
666
667         private void parseFunctionContext(JSFunctionElement aFunction)
668         {
669                 IToken token;
670
671                 token = nextNonWhitespaceToken();
672                 while (!token.isEOF())
673                 {
674                         int offset = scanner.getTokenOffset();
675                         int length = scanner.getTokenLength();
676                         String expression = getExpression(offset, length);
677
678                         if (expression.equals("}"))
679                         {
680                                 return;
681                         } else if (expression.equals("{"))
682                         {
683                                 parseFunctionContext(aFunction);
684                         } else if (token.equals(JSSyntaxScanner.TOKEN_DEFAULT))
685                         {
686                                 if (expression.equals("this"))
687                                 {
688                                         handleThisReference(aFunction.getName(), offset);
689                                 }
690                         }
691
692                         token = nextNonWhitespaceToken();
693                 }
694         }
695
696         private void detectFunctionContext(JSFunctionElement aFunction)
697         {
698                 IToken token = nextNonWhitespaceToken();
699                 while (!token.isEOF())
700                 {
701                         int offset = scanner.getTokenOffset();
702                         int length = scanner.getTokenLength();
703                         String expression = getExpression(offset, length);
704
705                         if (expression.equals("{"))
706                         {
707                                 parseFunctionContext(aFunction);
708                                 return;
709                         }
710
711                         token = nextNonWhitespaceToken();
712                 }
713         }
714
715         private JSInstanceMethodElement addInstanceMethod(
716                 String memberName,
717                 String className,
718                 String arguments,
719                 int classOffset,
720                 int functionOffset,
721                 int functionLength)
722         {
723                 int signatureLength = functionOffset - classOffset + functionLength;
724                 JSInstanceMethodElement aMethod =
725                         new JSInstanceMethodElement(this.sourceFile, memberName, arguments, classOffset, signatureLength);
726
727                 findOrCreateClass(className).addChildElement(aMethod);
728
729                 return aMethod;
730         }
731
732         private JSFunctionElement addClassMethod(
733                 String memberName,
734                 String className,
735                 String arguments,
736                 int classOffset,
737                 int functionOffset,
738                 int functionLength)
739         {
740                 JSClassElement aClass = findOrCreateClass(className); 
741                 int signatureLength = functionOffset - classOffset + functionLength;
742                 JSFunctionElement aMethod;
743                 
744                 if(aClass.isPrototype()) {
745                         aMethod = new JSInstanceMethodElement(this.sourceFile, memberName, arguments, classOffset, signatureLength);
746
747                         aClass.addChildElement(aMethod);
748                         detectInstanceMethodContext(className, aMethod);
749                 } else {
750                         aMethod = new JSClassMethodElement(this.sourceFile, memberName, arguments, classOffset, signatureLength);
751
752                         aClass.addChildElement(aMethod);
753                         detectClassMethodContext(aMethod);
754                 }
755
756                 return aMethod;
757         }
758
759         /**
760          * @param memberName
761          * @param className
762          * @param classOffset
763          * @return
764          */
765         private JSElement addClassVariable(String memberName, String className, int classOffset)
766         {
767                 //One extra char for "."
768                 JSElement aVariable;
769                 JSClassElement aClass = findOrCreateClass(className);
770                 
771                 if(aClass.isPrototype())
772                 {
773                         aVariable =     new JSInstanceVariableElement(this.sourceFile, memberName, classOffset, className.length() + memberName.length() + 1);
774
775                 } else {
776                         aVariable =     new JSClassVariableElement(this.sourceFile, memberName, classOffset, className.length() + memberName.length() + 1);
777                 }
778                 aClass.addChildElement(aVariable);
779
780                 return aVariable;
781         }
782
783         private JSInstanceVariableElement addInstanceVariable(
784                 String memberName,
785                 String className,
786                 int classOffset,
787                 int paddingWidth)
788         {
789                 //11 extra chars for ".prototype."
790                 JSInstanceVariableElement aVariable =
791                         new JSInstanceVariableElement(
792                                 this.sourceFile, 
793                                 memberName,
794                                 classOffset,
795                                 className.length() + memberName.length() + paddingWidth);
796
797                 findOrCreateClass(className).addChildElement(aVariable);
798
799                 return aVariable;
800         }
801
802         private JSGlobalVariableElement addGlobalVariable(String variableName, int offset)
803         {
804                 JSGlobalVariableElement aVariable;
805                 if (!globalVariables.containsKey(variableName))
806                 {
807                         aVariable = new JSGlobalVariableElement(this.sourceFile, variableName, offset, variableName.length());
808
809                         elementList.add(aVariable);
810                         globalVariables.put(variableName, aVariable);
811                 } else
812                 {
813                         aVariable = (JSGlobalVariableElement) classes.get(variableName);
814                 }
815
816                 return aVariable;
817         }
818
819         private JSClassElement findOrCreateClass(String className)
820         {
821                 JSClassElement aClass = null;
822                 if (!classes.containsKey(className))
823                 {
824                         if(functions.containsKey(className))
825                         {
826                                 //if we're creating a class from an existing function we must
827                                 //migrate the existing function to become a constructor in the class.
828                                 JSFunctionElement constructor = (JSFunctionElement) functions.get(className);
829         
830                                 aClass = new JSClassElement(this.sourceFile, className, constructor.getStart(), constructor.getLength());
831                                 aClass.addChildElement(constructor);
832         
833                                 elementList.remove(constructor);
834                                 elementList.add(aClass);
835                                 classes.put(className, aClass);
836                         } else if(globalVariables.containsKey(className))
837                         {
838                                 //if we're creating a class from an existing global variable we must
839                                 //migrate the existing function to become a constructor in the class.
840                                 JSGlobalVariableElement aVariable = (JSGlobalVariableElement) globalVariables.get(className);
841
842                                 aClass = new JSClassElement(this.sourceFile, className, aVariable.getStart(), aVariable.getLength());
843
844                                 elementList.remove(aVariable);
845                                 elementList.add(aClass);
846                                 classes.put(className, aClass);                         
847                                 globalVariables.remove(className);
848                         } else {
849                                 //The final case is if we have no idea where this class came from, but shouldn't be ignored.
850                                 aClass = new JSClassElement(this.sourceFile, className, 0, 0);
851
852                                 elementList.add(aClass);
853                                 classes.put(className, aClass);                         
854                         }
855                 } else
856                 {
857                         aClass = (JSClassElement) classes.get(className);
858                 }
859
860                 return aClass;
861         }
862         
863         public boolean isSystemClass(String aClassName)
864         {
865                 return systemClassMap.containsKey(aClassName);
866         }
867
868         /**
869          * Method getNaked.
870          * @param funcName
871          */
872         private String getNaked(String funcName)
873         {
874                 if (funcName == null)
875                 {
876                         return null;
877                 }
878
879                 funcName = funcName.trim().substring(FUNCTION.length()).trim();
880                 funcName = replaceInString(funcName.trim(), LINE_SEPARATOR, "");
881
882                 StringBuffer strBuf = new StringBuffer("");
883                 int len = funcName.length();
884                 boolean wasSpace = false;
885                 for (int i = 0; i < len; i++)
886                 {
887                         char ch = funcName.charAt(i);
888                         if (ch == ' ')
889                         {
890                                 wasSpace = true;
891                         } else // not space
892                                 {
893                                 if (wasSpace)
894                                 {
895                                         strBuf.append(' ');
896                                 }
897                                 strBuf.append(ch);
898                                 wasSpace = false;
899                         }
900                 }
901                 return strBuf.toString();
902         }
903
904         /**
905          * replace in a string a string sequence with another string sequence
906          */
907         public static String replaceInString(String source, String whatBefore, String whatAfter)
908         {
909                 if (null == source || source.length() == 0)
910                 {
911                         return source;
912                 }
913                 int beforeLen = whatBefore.length();
914                 if (beforeLen == 0)
915                 {
916                         return source;
917                 }
918                 StringBuffer result = new StringBuffer("");
919                 int lastIndex = 0;
920                 int index = source.indexOf(whatBefore, lastIndex);
921                 while (index >= 0)
922                 {
923                         result.append(source.substring(lastIndex, index));
924                         result.append(whatAfter);
925                         lastIndex = index + beforeLen;
926
927                         // get next
928                         index = source.indexOf(whatBefore, lastIndex);
929                 }
930                 result.append(source.substring(lastIndex));
931                 return result.toString();
932         }
933
934         /**
935          * @return Returns the elementList.
936          */
937         public List getElementList() {
938                 return elementList;
939         }
940
941 }