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