improved PHP parser
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / util / CommentRecorderParser.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v10.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.core.util;
12
13 import net.sourceforge.phpdt.internal.compiler.impl.CompilerOptions;
14 import net.sourceforge.phpdt.internal.compiler.parser.UnitParser;
15 import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter;
16 import net.sourceforge.phpdt.internal.compiler.problem.ProblemSeverities;
17 import net.sourceforge.phpeclipse.internal.compiler.ast.CompilationUnitDeclaration;
18
19 import org.eclipse.core.resources.IResource;
20
21 /**
22  * Internal parser used for parsing source to create DOM AST nodes.
23  * 
24  * @since 3.0
25  */
26 public class CommentRecorderParser extends UnitParser {
27         
28         // support for comments
29         int[] commentStops = new int[10];
30         int[] commentStarts = new int[10];
31         int commentPtr = -1; // no comment test with commentPtr value -1
32         protected final static int CommentIncrement = 100;
33
34         /**
35          * @param problemReporter
36          * @param optimizeStringLiterals
37          */
38         public CommentRecorderParser(ProblemReporter problemReporter) { // , boolean optimizeStringLiterals) {
39                 super(problemReporter); //, optimizeStringLiterals);
40         }
41
42         // old javadoc style check which doesn't include all leading comments into declaration
43         // for backward compatibility with 2.1 DOM 
44 //      public void checkComment() {
45 //
46 //              if (this.currentElement != null && this.scanner.commentPtr >= 0) {
47 //                      flushCommentsDefinedPriorTo(this.endStatementPosition); // discard obsolete comments
48 //              }
49 //              boolean deprecated = false;
50 //              boolean checkDeprecated = false;
51 //              int lastCommentIndex = -1;
52 //              
53 //              // 
54 //              
55 //              //since jdk1.2 look only in the last java doc comment...
56 //              nextComment : for (lastCommentIndex = this.scanner.commentPtr; lastCommentIndex >= 0; lastCommentIndex--){
57 //                      //look for @deprecated into the first javadoc comment preceeding the declaration
58 //                      int commentSourceStart = this.scanner.commentStarts[lastCommentIndex];
59 //                      // javadoc only (non javadoc comment have negative end positions.)
60 //                      if ((commentSourceStart < 0) ||
61 //                              (this.modifiersSourceStart != -1 && this.modifiersSourceStart < commentSourceStart) ||
62 //                              (this.scanner.commentStops[lastCommentIndex] < 0))
63 //                      {
64 //                              continue nextComment;
65 //                      }
66 //                      checkDeprecated = true;
67 //                      int commentSourceEnd = this.scanner.commentStops[lastCommentIndex] - 1; //stop is one over
68 //                      
69 //                      deprecated = this.javadocParser.checkDeprecation(commentSourceStart, commentSourceEnd);
70 //                      this.javadoc = this.javadocParser.docComment;
71 //                      break nextComment;
72 //              }
73 //              if (deprecated) {
74 //                      checkAndSetModifiers(AccDeprecated);
75 //              }
76 //              // modify the modifier source start to point at the first comment
77 //              if (lastCommentIndex >= 0 && checkDeprecated) {
78 //                      this.modifiersSourceStart = this.scanner.commentStarts[lastCommentIndex]; 
79 //                      if (this.modifiersSourceStart < 0) {
80 //                              this.modifiersSourceStart = -this.modifiersSourceStart;
81 //                      }
82 //              }
83 //
84 //      }
85
86 //      /* (non-Javadoc)
87 //       * @see net.sourceforge.phpdt.internal.compiler.parser.Parser#consumeClassHeader()
88 //       */
89 //      protected void consumeClassHeader() {
90 //              pushOnCommentsStack(0, this.scanner.commentPtr);
91 //              super.consumeClassHeader();
92 //      }
93 //      /* (non-Javadoc)
94 //       * @see net.sourceforge.phpdt.internal.compiler.parser.Parser#consumeEmptyClassMemberDeclaration()
95 //       */
96 //      protected void consumeEmptyClassMemberDeclaration() {
97 //              pushOnCommentsStack(0, this.scanner.commentPtr);
98 //              super.consumeEmptyClassMemberDeclaration();
99 //      }
100 //      /* (non-Javadoc)
101 //       * @see net.sourceforge.phpdt.internal.compiler.parser.Parser#consumeEmptyTypeDeclaration()
102 //       */
103 //      protected void consumeEmptyTypeDeclaration() {
104 //              pushOnCommentsStack(0, this.scanner.commentPtr);
105 //              super.consumeEmptyTypeDeclaration();
106 //      }
107 //      /* (non-Javadoc)
108 //       * @see net.sourceforge.phpdt.internal.compiler.parser.Parser#consumeInterfaceHeader()
109 //       */
110 //      protected void consumeInterfaceHeader() {
111 //              pushOnCommentsStack(0, this.scanner.commentPtr);
112 //              super.consumeInterfaceHeader();
113 //      }
114
115         /**
116          * Insure that start position is always positive.
117          * @see net.sourceforge.phpdt.internal.compiler.parser.Parser#containsComment(int, int)
118          */
119         public boolean containsComment(int sourceStart, int sourceEnd) {
120                 int iComment = this.scanner.commentPtr;
121                 for (; iComment >= 0; iComment--) {
122                         int commentStart = this.scanner.commentStarts[iComment];
123                         if (commentStart < 0) {
124                                 commentStart = -commentStart;
125                         }
126                         // ignore comments before start
127                         if (commentStart < sourceStart) continue;
128                         // ignore comments after end
129                         if (commentStart > sourceEnd) continue;
130                         return true;
131                 }
132                 return false;
133         }
134
135         /* (non-Javadoc)
136          * @see net.sourceforge.phpdt.internal.compiler.parser.Parser#endParse(int)
137          */
138         protected CompilationUnitDeclaration endParse(int act) {
139                 CompilationUnitDeclaration unit = super.endParse(act);
140                 if (unit.comments == null) {
141                         pushOnCommentsStack(0, this.scanner.commentPtr);
142                         unit.comments = getCommentsPositions();
143                 }
144                 return unit;
145         }
146
147         /* (non-Javadoc)
148          * Save all source comments currently stored before flushing them.
149          * @see net.sourceforge.phpdt.internal.compiler.parser.Parser#flushCommentsDefinedPriorTo(int)
150          */
151         public int flushCommentsDefinedPriorTo(int position) {
152
153                 int lastCommentIndex = this.scanner.commentPtr;
154                 if (lastCommentIndex < 0) return position; // no comment
155         
156                 // compute the index of the first obsolete comment
157                 int index = lastCommentIndex;
158                 int validCount = 0;
159                 while (index >= 0){
160                         int commentEnd = this.scanner.commentStops[index];
161                         if (commentEnd < 0) commentEnd = -commentEnd; // negative end position for non-javadoc comments
162                         if (commentEnd <= position){
163                                 break;
164                         }
165                         index--;
166                         validCount++;
167                 }
168                 // if the source at <position> is immediately followed by a line comment, then
169                 // flush this comment and shift <position> to the comment end.
170                 if (validCount > 0){
171                         int immediateCommentEnd = 0;
172                         while (index<lastCommentIndex && (immediateCommentEnd = -this.scanner.commentStops[index+1])  > 0){ // only tolerating non-javadoc comments (non-javadoc comment end positions are negative)
173                                 // is there any line break until the end of the immediate comment ? (thus only tolerating line comment)
174                                 immediateCommentEnd--; // comment end in one char too far
175                                 if (this.scanner.getLineNumber(position) != this.scanner.getLineNumber(immediateCommentEnd)) break;
176                                 position = immediateCommentEnd;
177                                 validCount--; // flush this comment
178                                 index++;
179                         }
180                 }
181         
182                 if (index < 0) return position; // no obsolete comment
183                 pushOnCommentsStack(0, index); // store comment before flushing them
184
185                 if (validCount > 0){ // move valid comment infos, overriding obsolete comment infos
186                         System.arraycopy(this.scanner.commentStarts, index + 1, this.scanner.commentStarts, 0, validCount);
187                         System.arraycopy(this.scanner.commentStops, index + 1, this.scanner.commentStops, 0, validCount);               
188                 }
189                 this.scanner.commentPtr = validCount - 1;
190                 return position;
191         }
192
193         /*
194          * Build a n*2 matrix of comments positions.
195          * For each position, 0 is for start position and 1 for end position of the comment.
196          */
197         public int[][] getCommentsPositions() {
198                 int[][] positions = new int[this.commentPtr+1][2];
199                 for (int i = 0, max = this.commentPtr; i <= max; i++){
200                         positions[i][0] = this.commentStarts[i];
201                         positions[i][1] = this.commentStops[i];
202                 }
203                 return positions;
204         }
205
206         /* (non-Javadoc)
207          * @see net.sourceforge.phpdt.internal.compiler.parser.Parser#initialize()
208          */
209         public void initialize(boolean phpMode) {
210                 super.initialize(phpMode);
211                 this.commentPtr = -1;
212         }
213         
214         /* (non-Javadoc)
215          * Create and store a specific comment recorder scanner.
216          * @see net.sourceforge.phpdt.internal.compiler.parser.Parser#initializeScanner()
217          */
218         public void initializeScanner() {
219                 this.scanner = new CommentRecorderScanner(
220                                 false /*comment*/, 
221                                 false /*whitespace*/, 
222                                 this.options.getSeverity(CompilerOptions.NonExternalizedString) != ProblemSeverities.Ignore /*nls*/, 
223 //                              this.options.sourceLevel /*sourceLevel*/, 
224                                 this.options.taskTags/*taskTags*/,
225                                 this.options.taskPriorites);/*taskPriorities*/
226 //                              this.options.isTaskCaseSensitive/*taskCaseSensitive*/);
227         }
228
229         /*
230          * Push all stored comments in stack.
231          */
232         private void pushOnCommentsStack(int start, int end) {
233         
234                 for (int i=start; i<=end; i++) {
235                         // First see if comment hasn't been already stored
236                         int scannerStart = this.scanner.commentStarts[i]<0 ? -this.scanner.commentStarts[i] : this.scanner.commentStarts[i];
237                         int commentStart = this.commentPtr == -1 ? -1 : (this.commentStarts[this.commentPtr]<0 ? -this.commentStarts[this.commentPtr] : this.commentStarts[this.commentPtr]);
238                         if (commentStart == -1 ||  scannerStart > commentStart) {
239                                 try {
240                                         this.commentPtr++;
241                                         this.commentStarts[this.commentPtr] = this.scanner.commentStarts[i];
242                                         this.commentStops[this.commentPtr] = this.scanner.commentStops[i];
243                                 } catch (IndexOutOfBoundsException e) {
244                                         // this.commentPtr is still correct 
245                                         int oldStackLength = this.commentStarts.length;
246                                         int oldCommentStarts[] = this.commentStarts;
247                                         this.commentStarts = new int[oldStackLength + CommentIncrement];
248                                         System.arraycopy(oldCommentStarts, 0, this.commentStarts, 0, oldStackLength);
249                                         this.commentStarts[this.commentPtr] = this.scanner.commentStarts[i];
250                                         int oldCommentStops[] = this.commentStops;
251                                         this.commentStops = new int[oldStackLength + CommentIncrement];
252                                         System.arraycopy(oldCommentStops, 0, this.commentStops, 0, oldStackLength);
253                                         this.commentStops[this.commentPtr] = this.scanner.commentStops[i];
254                                 }
255                         }
256                 }
257         }
258         /* (non-Javadoc)
259          * Save all source comments currently stored before flushing them.
260          * @see net.sourceforge.phpdt.internal.compiler.parser.Parser#resetModifiers()
261          */
262         protected void resetModifiers() {
263                 pushOnCommentsStack(0, this.scanner.commentPtr);
264                 super.resetModifiers();
265         }
266 }