uidesigner/com.nokia.sdt.sourcegen/src/com/nokia/sdt/sourcegen/core/ParseUtils.java
changeset 0 fb279309251b
equal deleted inserted replaced
-1:000000000000 0:fb279309251b
       
     1 /*
       
     2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of the License "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description: 
       
    15 *
       
    16 */
       
    17 /**
       
    18  * 
       
    19  */
       
    20 package com.nokia.sdt.sourcegen.core;
       
    21 
       
    22 import com.nokia.cpp.internal.api.utils.core.Check;
       
    23 import com.nokia.cpp.internal.api.utils.core.Tuple;
       
    24 
       
    25 
       
    26 /**
       
    27  * Utilities for parsing source text.
       
    28  * 
       
    29  *
       
    30  */
       
    31 public class ParseUtils {
       
    32 
       
    33 	/** Find insert position in the given range before the given character, flush
       
    34 	 * to the start of the line */
       
    35 	public static int getInsertPositionAtEnd(char[] text, int start, int length, char lastCh) {
       
    36 	    int end = start + length;
       
    37 	    int idx;
       
    38 	    for (idx = end - 1; idx >= start; idx--) {
       
    39 	        if (text[idx] == lastCh) {
       
    40 	            break;
       
    41 	        }
       
    42 	    }
       
    43 	    // now go back to a non-space character (probably a newline)
       
    44 	    while (idx > start && (text[idx-1] == '\t' || text[idx-1] == ' ')) {
       
    45 	        idx--;
       
    46 	    }
       
    47 	    if (idx > start && text[idx] != lastCh && text[idx-1] != '\n')
       
    48 	        idx++;
       
    49 	    
       
    50 	    //System.out.println("write at " +idx+": " + new String(text, idx, end - idx));
       
    51 	    return idx;
       
    52 	}
       
    53 
       
    54 	/** Find insert position in the given range before the given character, flush
       
    55 	 * to the start of the line */
       
    56 	public static int getInsertPositionAtStart(char[] text, int start, int length, char firstCh) {
       
    57 	    int end = start + length;
       
    58 	    int idx;
       
    59 	    for (idx = start; idx < end; idx++) {
       
    60 	        if (text[idx] == firstCh) {
       
    61 	            break;
       
    62 	        }
       
    63 	    }
       
    64 	    // now go back to a non-space character (probably a newline)
       
    65 	    while (idx > start && (text[idx-1] == '\t' || text[idx-1] == ' ')) {
       
    66 	        idx--;
       
    67 	    }
       
    68 	    if (idx > start && text[idx] != firstCh && text[idx-1] != '\n')
       
    69 	        idx++;
       
    70 	    
       
    71 	    //System.out.println("write at " +idx+": " + new String(text, idx, end - idx));
       
    72 	    return idx;
       
    73 	}
       
    74 
       
    75 	/** Find insert position in the given range after the given character, flush
       
    76 	 * to the first text in the line */
       
    77 	public static int getInsertPositionAfterStart(char[] text, int start, int length, char firstCh) {
       
    78 	    int end = start + length;
       
    79 	    int idx;
       
    80 	    for (idx = start; idx < end; idx++) {
       
    81 	        if (text[idx] == firstCh) {
       
    82 	        	idx++;
       
    83 	            break;
       
    84 	        }
       
    85 	    }
       
    86 	    
       
    87 	    // skip to the first non-whitespace character
       
    88 	    while (idx < end && Character.isWhitespace(text[idx]))
       
    89 	    	idx++;
       
    90 	
       
    91 	    return idx;
       
    92 	}
       
    93 
       
    94 	/**
       
    95 	 * Get length of newline. 
       
    96 	 * @param text
       
    97 	 * @param offset offset of a '\r' or '\n' character
       
    98 	 * @return length of sequence
       
    99 	 */
       
   100 	public static int newlineLengthBefore(char[] text, int offset) {
       
   101 	    if (offset <= 0)
       
   102 	        return 0;
       
   103 	    if (text[offset - 1] == '\r')
       
   104 	        return 1;
       
   105 	    else /* must be '\n' */ if (offset > 1 && text[offset - 2] == '\r')
       
   106 	        return 2;
       
   107 	    else
       
   108 	        return 1;
       
   109 	}
       
   110 
       
   111 	/**
       
   112 	 * Get length of newline. 
       
   113 	 * @param text
       
   114 	 * @param offset of a '\r' or '\n' character
       
   115 	 * @return length of sequence
       
   116 	 */
       
   117 	public static int newlineLength(char[] text, int offset) {
       
   118 	    if (offset >= text.length)
       
   119 	        return 0;
       
   120 	    else if (text[offset] == '\n')
       
   121 	        return 1;
       
   122 	    else /* must be \r */ if (offset + 1 < text.length && text[offset + 1] == '\n')
       
   123 	        return 2;
       
   124 	    else
       
   125 	        return 1;
       
   126 	}
       
   127 
       
   128 	/**
       
   129 	 * Scan backwards to find the start of a line,
       
   130 	 * skipping any block comments (which may have their own
       
   131 	 * newlines embedded)
       
   132 	 * @param text
       
   133 	 * @param start
       
   134 	 * @return offset at start of line
       
   135 	 */
       
   136 	public static int skipToLogicalBeginningOfLineBackward(char[] text, int start, int limit) {
       
   137 	    while (start > limit) {
       
   138 	        if (text[start-1] == '\n' || text[start-1] == '\r')
       
   139 	            break;
       
   140 	        if (start - 1 > limit && text[start-2] == '*' && text[start-1] == '/') {
       
   141 	            // scan backward past block comment
       
   142 	            start --;
       
   143 	            while (start > limit) {
       
   144 	                if (text[start - 1] == '/' && text[start] == '*')
       
   145 	                    break;
       
   146 	                start--;
       
   147 	            }
       
   148 	        }
       
   149 	        start--;
       
   150 	    }
       
   151 	    return start;
       
   152 	}
       
   153 
       
   154 	/**
       
   155 	 * Scan forward to find the end of a line (excluding terminators),
       
   156 	 * skipping any block comments (which may have their own
       
   157 	 * newlines embedded)
       
   158 	 * @param text
       
   159 	 * @param end current position from which to locate end of line
       
   160 	 * @param limit upper bound to search
       
   161 	 * @return offset of terminator at end of line  
       
   162 	 */
       
   163 	public static int skipToLogicalEndOfLineForward(char[] text, int end, int limit) {
       
   164 	    while (end < limit) {
       
   165 	        if (text[end] == '\n' || text[end] == '\r')
       
   166 	            break;
       
   167 	        if (end + 1 < limit && text[end] == '/' && text[end+1] == '*') {
       
   168 	            // scan forward past block comment
       
   169 	            end += 2;
       
   170 	            while (end + 1 < limit) {
       
   171 	                if (text[end] == '*' && text[end + 1] == '/')
       
   172 	                    break;
       
   173 	                end++;
       
   174 	            }
       
   175 	            end += 2; // skip '*' and '/'
       
   176 	        } else
       
   177 	        	end++;
       
   178 	    }
       
   179 	    return end;
       
   180 	}
       
   181 
       
   182 	/**
       
   183 	 * Skip to and past the end of line
       
   184 	 * @param text
       
   185 	 * @param start
       
   186 	 * @param endIdx
       
   187 	 * @return position of next line
       
   188 	 */
       
   189 	public static int skipToNextLine(char[] text, int start, int endIdx) {
       
   190 		while (start < endIdx) {
       
   191 			if (text[start] == '\r') {
       
   192 				if (start + 1 < endIdx && text[start + 1] == '\n') {
       
   193 					start += 2;
       
   194 				} else {
       
   195 					start++;
       
   196 				}
       
   197 				break;
       
   198 			} else if (text[start] == '\n') {
       
   199 				start++;
       
   200 				break;
       
   201 			} else
       
   202 				start++;
       
   203 		}
       
   204 	
       
   205 		return start;
       
   206 	}
       
   207 
       
   208 	/**
       
   209 	 * Skip to and past the end of line
       
   210 	 * @param text
       
   211 	 * @param start
       
   212 	 * @return position of next line
       
   213 	 */
       
   214 	public static int skipToNextLine(CharSequence text, int start) {
       
   215 		int endIdx = text.length();
       
   216 		while (start < endIdx) {
       
   217 			char ch = text.charAt(start);
       
   218 			if (ch == '\r') {
       
   219 				if (start + 1 < endIdx && text.charAt(start + 1) == '\n') {
       
   220 					start += 2;
       
   221 				} else {
       
   222 					start++;
       
   223 				}
       
   224 				break;
       
   225 			} else if (ch == '\n') {
       
   226 				start++;
       
   227 				break;
       
   228 			} else
       
   229 				start++;
       
   230 		}
       
   231 	
       
   232 		return start;
       
   233 	}
       
   234 
       
   235 	/**
       
   236 	 * Skip a newline if present
       
   237 	 * @param subText
       
   238 	 * @param i
       
   239 	 */
       
   240 	public static int skipIfNewLine(CharSequence subText, int i) {
       
   241 		if (i < subText.length()) {
       
   242 			char ch = subText.charAt(i); 
       
   243 			if (ch == '\r') {
       
   244 				if (i + 1 < subText.length() && subText.charAt(i+1) == '\n')
       
   245 					return i + 2;
       
   246 				else
       
   247 					return i + 1;
       
   248 			}
       
   249 			else if (ch == '\n')
       
   250 				return i + 1;
       
   251 		}
       
   252 		return i;
       
   253 	}
       
   254 
       
   255 	/**
       
   256 	 * Skip a newline if present
       
   257 	 * @param subText
       
   258 	 * @param i incoming offset
       
   259 	 * @return new offset
       
   260 	 */
       
   261 	public static int skipIfNewLine(char[] text, int i) {
       
   262 		if (i < text.length) {
       
   263 			char ch = text[i]; 
       
   264 			if (ch == '\r') {
       
   265 				if (i + 1 < text.length && text[i+1] == '\n')
       
   266 					return i + 2;
       
   267 				else
       
   268 					return i + 1;
       
   269 			}
       
   270 			else if (ch == '\n')
       
   271 				return i + 1;
       
   272 		}
       
   273 		return i;
       
   274 	}
       
   275 
       
   276 	/**
       
   277 	 * Scan forward to skip a comment, either a block comment or
       
   278 	 * a C++ single line comment.<p>  
       
   279 	 * @param text
       
   280 	 * @param end current position from which to locate comments
       
   281 	 * @param limit upper bound to search
       
   282 	 * @param includeTrailingSpace if true, skip whitespace and include
       
   283 	 * any trailing newline; else, stop if no comment found on line. 
       
   284 	 * @return new end offset   
       
   285 	 */
       
   286 	public static int includeTrailingComment(char[] text, int end, int limit, boolean includeTrailingSpace) {
       
   287 		int currentEoc = end;
       
   288 	    while (end < limit) {
       
   289 	        if (text[end] == '\n' || text[end] == '\r') {
       
   290 	        	if (includeTrailingSpace)
       
   291 	        		currentEoc = skipIfNewLine(text, end);
       
   292 	            break;
       
   293 	        }
       
   294 	        if (end + 1 < limit && text[end] == '/' && text[end+1] == '*') {
       
   295 	            // scan forward past block comment
       
   296 	            end += 2;
       
   297 	            while (end + 1 < limit) {
       
   298 	                if (text[end] == '*' && text[end + 1] == '/')
       
   299 	                    break;
       
   300 	                end++;
       
   301 	            }
       
   302 	            end += 2; // skip '*' and '/'
       
   303 	            currentEoc = end;
       
   304 	        } else if (end + 1 < limit && text[end] == '/' && text[end+1] == '/') {
       
   305 	        	// scan forward past end of line comment
       
   306 	        	end += 2;
       
   307 	        	while (end < limit && text[end] != '\r' && text[end] != '\n')
       
   308 	        		end++;
       
   309 	        	if (includeTrailingSpace)
       
   310 	        		currentEoc = skipIfNewLine(text, end);
       
   311 	        	else
       
   312 	        		currentEoc = end;
       
   313 	        	break;
       
   314 	        } else if (text[end] == ' ' || text[end] == '\t')
       
   315 	        	end++;
       
   316 	        else
       
   317 	        	break;
       
   318 	    }
       
   319 	    return currentEoc;
       
   320 	}
       
   321 
       
   322 	/**
       
   323 	 * Scan backward past any comments (single-line or block) and stop at the
       
   324 	 * beginning of a line. Do not include any comments that live at the end of
       
   325 	 * another line. Do not include blank lines if they do not have
       
   326 	 * comments preceding. Stop if any non-whitespace encountered.
       
   327 	 * 
       
   328 	 * @param text
       
   329 	 * @param start
       
   330 	 *            current position from which to search backward
       
   331 	 * @param limit
       
   332 	 *            lower bound to search
       
   333 	 * @param includeLeadingSpace if true, include whitespace before any comment
       
   334 	 * @return offset of beginning of line
       
   335 	 */
       
   336 	public static int includeLeadingComment(char[] text, int start, int limit, boolean includeLeadingSpace) {
       
   337 		// The algorithm works on a logical line-by-line basis,
       
   338 		// where a logical line may be several lines spanned by
       
   339 		// a block comment.
       
   340 		//
       
   341 		// We scan whitespace backward.  Non-whitespace may be
       
   342 		// '*/', which prompts us to find a the beginning, and 
       
   343 		// continue.  Or it may be other text, in which case
       
   344 		// we scan backward for '//'.
       
   345 		// we temporarily skip to the beginning of the line
       
   346 		// and verify that 
       
   347 		
       
   348 		int currentLineStart = start;
       
   349 		
       
   350 		if (includeLeadingSpace)
       
   351 			currentLineStart = skipOnlyWhitespaceToLineStart(text, currentLineStart, limit); 
       
   352 		
       
   353 		while (start > limit) {
       
   354 			int prevCommentStart = findPreviousComment(text, currentLineStart, limit, true);
       
   355 			// this check includes prevCommentStart==-1
       
   356 			if (prevCommentStart >= limit) {
       
   357 				currentLineStart = prevCommentStart;
       
   358 				if (includeLeadingSpace)
       
   359 					currentLineStart = skipOnlyWhitespaceToLineStart(text, currentLineStart, limit); 
       
   360 			} else
       
   361 				break;
       
   362 		}
       
   363 		return currentLineStart;
       
   364 	}
       
   365 
       
   366 	/**
       
   367 	 * Find a comment preceding the cursor
       
   368 	 * @param text
       
   369 	 * @param currentLineStart
       
   370 	 * @param limit
       
   371 	 * @param mustBeAloneOnLine true: comment cannot have text preceding on line
       
   372 	 * @return
       
   373 	 */
       
   374 	private static int findPreviousComment(char[] text, int start, int limit, boolean mustBeAloneOnLine) {
       
   375 		int currentBoc = -1;
       
   376 		boolean lastWasBlockComment = false;
       
   377 		while (start >= limit) {
       
   378 			if (start == limit) {
       
   379 				if (currentBoc >= limit)
       
   380 					return currentBoc;
       
   381 				else
       
   382 					break;
       
   383 			}
       
   384 	        start--;
       
   385 	        if (text[start] == '\r' || text[start] == '\n') {
       
   386 	        	// were we waiting to verify that a comment was alone
       
   387 	        	// on the line (preceded perhaps by other one-line or block comments?)
       
   388 	        	if (currentBoc >= limit)
       
   389 	        		return currentBoc;
       
   390 	        } else if (text[start] == ' ' || text[start] == '\t') {
       
   391 	        	// skip spaces
       
   392 	        } else if (start > limit && text[start-1] == '*' && text[start] == '/') {
       
   393 	            // scan backward past block comment
       
   394 	            while (start > limit) {
       
   395 	            	start--;
       
   396 	                if (text[start] == '/' && text[start+1] == '*') {
       
   397 	                    break;
       
   398 	                }
       
   399 	            }
       
   400 	            currentBoc = start;
       
   401 	            lastWasBlockComment = true;
       
   402 	            if (!mustBeAloneOnLine)
       
   403 	            	return currentBoc;
       
   404 	        } else {
       
   405 	        	// not whitespace -- better be a single-line comment alone on the line
       
   406 	        	int ptr = start;
       
   407 	        	while (ptr >= limit) {
       
   408 	        		if (text[ptr] == '/' && text[ptr - 1] == '/') {
       
   409 	        			ptr--;
       
   410 	        			// thee may be multiple one-line comments but we only want the last 
       
   411 	        			if (currentBoc == -1) {
       
   412 	        				currentBoc = ptr;
       
   413 	        	            lastWasBlockComment = false;
       
   414 	        	            if (!mustBeAloneOnLine)
       
   415 	        	            	return currentBoc;
       
   416 	        			}	        				
       
   417 	        			break;
       
   418 	        		}
       
   419 	        		if (text[ptr] == '\r' || text[ptr] == '\n') {
       
   420 	        			// oops, no comment at all on the line
       
   421 	        			return -1;
       
   422 	        		}
       
   423 	        		ptr--;
       
   424 	        	}
       
   425 	        	start = ptr;
       
   426 	        }
       
   427 		}
       
   428 		
       
   429 		return lastWasBlockComment ? currentBoc : -1;
       
   430 	}
       
   431 
       
   432 	/**
       
   433 	 * Skip to line start but only if only whitespace is encountered.
       
   434 	 * @param text
       
   435 	 * @param currentLineStart
       
   436 	 * @param limit
       
   437 	 * @return
       
   438 	 */
       
   439 	private static int skipOnlyWhitespaceToLineStart(char[] text, int start, int limit) {
       
   440 		int orig = start;
       
   441 		while (start > limit) {
       
   442 			start--;
       
   443 			if (text[start] == '\r' || text[start] == '\n')
       
   444 				return start+1;
       
   445 			if (text[start] != ' ' && text[start] != '\t')
       
   446 				return orig;
       
   447 		}
       
   448 		return limit;
       
   449 	}
       
   450 
       
   451 	/**
       
   452 	 * Skip whitespace in text, including comments.  
       
   453 	 * @return offset of non-whitespace or newline
       
   454 	 */
       
   455 	public static int skipWhitespaceForward(char[] text, int end, int limit) {
       
   456 	    while (end < limit) {
       
   457 	        if (end + 1 < limit && text[end] == '/' && text[end+1] == '*') {
       
   458 	            // scan forward past block comment
       
   459 	            end += 2;
       
   460 	            while (end + 1 < limit) {
       
   461 	                if (text[end] == '*' && text[end + 1] == '/')
       
   462 	                    break;
       
   463 	                end++;
       
   464 	            }
       
   465 	            end += 2; // skip '*' and '/'
       
   466 	        } else if (end + 1 < limit && text[end] == '/' && text[end+1] == '/') {
       
   467 	        	// scan forward past end of line comment
       
   468 	        	end += 2;
       
   469 	        	while (end < limit && text[end] != '\r' && text[end] != '\n')
       
   470 	        		end++;
       
   471 	        	break;
       
   472 	        } else if (text[end] == ' ' || text[end] == '\t')
       
   473 	        	end++;
       
   474 	        else
       
   475 	        	break;
       
   476 	    }
       
   477 	    return end;
       
   478 	}
       
   479 
       
   480 	/**
       
   481 	 * Match the given string in the array and return the updated position.
       
   482 	 * 
       
   483 	 * @return new position or pos if not matched
       
   484 	 */
       
   485 	public static int matchText(String str, char[] text, int pos, int limit) {
       
   486 		int i;
       
   487 		for (i = 0; i < str.length(); i++) {
       
   488 			if (pos + i >= limit || text[pos + i] != str.charAt(i))
       
   489 				return pos;
       
   490 		}
       
   491 		return pos + i;
       
   492 	}
       
   493 
       
   494 	/**
       
   495 	 * Search for a directive backwards.  This is identified by
       
   496 	 * a hash mark abutting whitespace after a line start.
       
   497 	 * @param text
       
   498 	 * @param ptr upper barrier
       
   499 	 * @param limit lower barrier
       
   500 	 * @return offset or -1 
       
   501 	 */
       
   502 	public static int searchDirectiveBackwards(char[] text, int ptr, int limit) {
       
   503 		char quote = 0;
       
   504 		while (ptr > limit) {
       
   505 			ptr--;
       
   506 			if (quote == text[ptr]) {
       
   507 				quote = 0;
       
   508 			} else if (text[ptr] == '#') {
       
   509 				int line = skipToLogicalBeginningOfLineBackward(text, ptr, limit);
       
   510 				if (skipWhitespaceForward(text, line, ptr) == ptr) {
       
   511 					return ptr;
       
   512 				}
       
   513 			} else if (text[ptr] == '\'' || text[ptr] == '#')
       
   514 				quote = text[ptr];
       
   515 		}
       
   516 		return -1;
       
   517 	}
       
   518 
       
   519 	/**
       
   520 	 * Search for a directive forwards.  This is identified by
       
   521 	 * a hash mark abutting whitespace after a line start.
       
   522 	 * @param text
       
   523 	 * @param ptr current position (which may be the directive)
       
   524 	 * @param limit upper barrier
       
   525 	 * @return offset or -1 
       
   526 	 */
       
   527 	public static int searchDirectiveForwards(char[] text, int ptr, int limit) {
       
   528 		while (ptr < limit) {
       
   529 			ptr = skipToLogicalEndOfLineForward(text, ptr, limit);
       
   530 			if (ptr < limit) {
       
   531 				ptr += newlineLength(text, ptr);
       
   532 				ptr = skipWhitespaceForward(text, ptr, limit);
       
   533 				if (ptr < limit) {
       
   534 					if (text[ptr] == '#')
       
   535 						return ptr;
       
   536 				}
       
   537 			}
       
   538 		}
       
   539 		return -1;
       
   540 	}
       
   541 
       
   542 	/**
       
   543 	 * Find a named directive after the cursor and return its argument
       
   544 	 * with any comments and surrounding space removed.
       
   545 	 * @param name
       
   546 	 * @param start pointer to '#'
       
   547 	 * @param limit
       
   548 	 * @return tuple of (trimmed argument, directive start, directive end,
       
   549 	 * argument start, argument end) or null if no match
       
   550 	 */
       
   551 	public static Tuple findDirectiveAndArgument(String name, char[] text, int start, int limit) {
       
   552 		int dirStart = -1, dirEnd = -1, argStart = -1, argEnd = -1;
       
   553 		Check.checkArg(text[start] == '#');
       
   554 		start++;
       
   555 		start = skipWhitespaceForward(text, start, limit);
       
   556 		if (start + name.length() < limit) {
       
   557 			for (int i = 0; i < name.length(); i++)
       
   558 				if (text[start + i] != name.charAt(i))
       
   559 					return null;
       
   560 			dirStart = start;
       
   561 			start += name.length();
       
   562 			dirEnd = start;
       
   563 			start = skipWhitespaceForward(text, start, limit);
       
   564 			
       
   565 			argStart = start;
       
   566 			int ptr = start;
       
   567 			while (ptr < limit) {
       
   568 				if (text[ptr] == '\r' || text[ptr] == '\n')
       
   569 					break;
       
   570 				int newPtr = skipWhitespaceForward(text, ptr, limit);
       
   571 				if (newPtr == ptr)
       
   572 					ptr++; // not whitespace
       
   573 				else
       
   574 					ptr = newPtr;
       
   575 			}
       
   576 			
       
   577 			argEnd = ptr;
       
   578 
       
   579 			// there may be comments at EOL; ignore them when deciding what the directive
       
   580 			// argument's range is
       
   581 			while (true) {
       
   582 				int prevComment = findPreviousComment(text, ptr, argStart, false);
       
   583 				if (prevComment == -1)
       
   584 					break;
       
   585 				while (prevComment > argStart && Character.isWhitespace(text[prevComment-1]))
       
   586 					prevComment--;
       
   587 				argEnd = prevComment;
       
   588 				ptr = argEnd;
       
   589 			}
       
   590 				
       
   591 			String arg = new String(text, argStart, argEnd - argStart);
       
   592 			//arg = CdtUtils.stripComments(arg).toString().trim();
       
   593 			return new Tuple(arg, dirStart, dirEnd, argStart, argEnd);
       
   594 		}
       
   595 		return null;
       
   596 	}
       
   597 
       
   598 
       
   599 }