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