PLSQL/Annotation/src/org/netbeans/modules/plsql/annotation/PlsqlMethodAnnotationUtil.java
author Subhashini Sooriarachchi <subslk@netbeans.org>
Mon, 12 Aug 2013 16:43:18 +0530
changeset 465 fed38113b171
parent 423 86eb9da4244a
permissions -rw-r--r--
EADS-3910 PLSQL Hints - Functions without RETURN statement (SAMWIN)
     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 2011 Oracle and/or its affiliates. All rights reserved.
     5  *
     6  * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
     7  * Other names may be trademarks of their respective owners.
     8  *
     9  * The contents of this file are subject to the terms of either the GNU
    10  * General Public License Version 2 only ("GPL") or the Common
    11  * Development and Distribution License("CDDL") (collectively, the
    12  * "License"). You may not use this file except in compliance with the
    13  * License. You can obtain a copy of the License at
    14  * http://www.netbeans.org/cddl-gplv2.html
    15  * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    16  * specific language governing permissions and limitations under the
    17  * License.  When distributing the software, include this License Header
    18  * Notice in each file and include the License file at
    19  * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    20  * particular file as subject to the "Classpath" exception as provided
    21  * by Oracle in the GPL Version 2 section of the License file that
    22  * accompanied this code. If applicable, add the following below the
    23  * License Header, with the fields enclosed by brackets [] replaced by
    24  * your own identifying information:
    25  * "Portions Copyrighted [year] [name of copyright owner]"
    26  *
    27  * If you wish your version of this file to be governed by only the CDDL
    28  * or only the GPL Version 2, indicate your decision by adding
    29  * "[Contributor] elects to include this software in this distribution
    30  * under the [CDDL or GPL Version 2] license." If you do not indicate a
    31  * single choice of license, a recipient has the option to distribute
    32  * your version of this file under either the CDDL, the GPL Version 2 or
    33  * to extend the choice of license to its licensees as provided above.
    34  * However, if you add GPL Version 2 code and therefore, elected the GPL
    35  * Version 2 license, then the option applies only if the new code is
    36  * made subject to such option by the copyright holder.
    37  *
    38  * Contributor(s):
    39  *
    40  * Portions Copyrighted 2011 Sun Microsystems, Inc.
    41  */
    42 package org.netbeans.modules.plsql.annotation;
    43 
    44 import org.netbeans.modules.plsql.lexer.PlsqlBlock;
    45 import org.netbeans.modules.plsql.lexer.PlsqlBlockType;
    46 import org.netbeans.modules.plsql.lexer.PlsqlTokenId;
    47 import org.netbeans.modules.plsql.utilities.PlsqlParserUtil;
    48 import java.util.ArrayList;
    49 import java.util.Collections;
    50 import java.util.Comparator;
    51 import java.util.List;
    52 import java.util.Locale;
    53 import javax.swing.JOptionPane;
    54 import javax.swing.text.BadLocationException;
    55 import javax.swing.text.Document;
    56 import org.netbeans.api.lexer.Token;
    57 import org.netbeans.api.lexer.TokenHierarchy;
    58 import org.netbeans.api.lexer.TokenSequence;
    59 import org.netbeans.modules.editor.NbEditorUtilities;
    60 import org.openide.windows.WindowManager;
    61 
    62 /**
    63  * Util class for annotations added for methods
    64  *
    65  * @author YADHLK
    66  */
    67 public class PlsqlMethodAnnotationUtil {
    68 
    69     static int hasReturn = 0;
    70     private static final int HAS_RETURNS = 1;
    71     private static final int NO_RETURNS = 2;
    72     private static Comparator<PlsqlBlock> comparator = new Comparator<PlsqlBlock>() {
    73         @Override
    74         public int compare(PlsqlBlock o1, PlsqlBlock o2) {
    75             Integer o1pos, o2pos;
    76             if (o1.getStartOffset() > -1 && o2.getStartOffset() > -1) {
    77                 o1pos = new Integer(o1.getStartOffset());
    78                 o2pos = new Integer(o2.getStartOffset());
    79             } else {
    80                 o1pos = new Integer(o1.getEndOffset());
    81                 o2pos = new Integer(o2.getEndOffset());
    82             }
    83             return o1pos.compareTo(o2pos);
    84         }
    85     };
    86 
    87     public static int getOffsetToInsert(final Document doc, final int startOffset, final int endOffset) {
    88         final TokenHierarchy tokenHierarchy = TokenHierarchy.get(doc);
    89         @SuppressWarnings("unchecked")
    90         final TokenSequence<PlsqlTokenId> ts = tokenHierarchy.tokenSequence(PlsqlTokenId.language());
    91         int offset = -1;
    92 
    93         if (ts != null) {
    94             ts.move(startOffset);
    95             Token<PlsqlTokenId> token = ts.token();
    96             while (ts.moveNext() && ts.offset() < endOffset) {
    97                 token = ts.token();
    98                 if ((token.id() == PlsqlTokenId.KEYWORD)
    99                         && (token.text().toString().equalsIgnoreCase("BEGIN"))) {
   100                     if (ts.moveNext()) {
   101                         offset = ts.offset();
   102                     }
   103                     break;
   104                 }
   105             }
   106         }
   107         return offset;
   108     }
   109 
   110     public static PlsqlBlock findMatchingMethod(final List<PlsqlBlock> blockHier, final Document source,
   111             final Document dest, final PlsqlBlock sourceBlock) {
   112         PlsqlBlock match = null;
   113         final List<PlsqlBlock> matchList = new ArrayList<PlsqlBlock>();
   114         PlsqlParserUtil.findMatchingDefBlocks(blockHier, sourceBlock.getName(), matchList);
   115 
   116         //There are several methods with the same name. Have to check the signature
   117         final List<String> usageParams = PlsqlParserUtil.fetchMethodDefParamTypes(source, sourceBlock.getStartOffset());
   118 
   119         //Take PlsqlBlock one by one and compare the parameters
   120         for (int x = 0; x < matchList.size(); x++) {
   121             final PlsqlBlock block = matchList.get(x);
   122             if (!(block.getParent() != null && sourceBlock.getParent() != null ? block.getParent().getName().equalsIgnoreCase(sourceBlock.getParent().getName()) : true)) {
   123                 continue; //If the parent block name is not the same this is not a match
   124             }
   125 
   126             final List<String> params = PlsqlParserUtil.fetchMethodDefParamTypes(dest, block.getStartOffset());
   127             final int defaultNo = PlsqlParserUtil.fetchDefaultParams(dest, block.getStartOffset());
   128             if (PlsqlParserUtil.compareParameters(usageParams, params, defaultNo)) {
   129                 match = block;
   130                 break;
   131             }
   132         }
   133 
   134         return match;
   135     }
   136 
   137     public static String getMethodSpecification(final Document doc, final PlsqlBlock block) {
   138         String methodSpec = "";
   139         final TokenHierarchy tokenHierarchy = TokenHierarchy.get(doc);
   140         @SuppressWarnings("unchecked")
   141         final TokenSequence<PlsqlTokenId> ts = tokenHierarchy.tokenSequence(PlsqlTokenId.language());
   142 
   143         if (ts != null) {
   144             ts.move(block.getStartOffset());
   145             Token<PlsqlTokenId> token = ts.token();
   146             while (ts.moveNext() && block.getEndOffset() > ts.offset()) {
   147                 token = ts.token();
   148                 if (token.text().toString().equalsIgnoreCase("IS")) {
   149                     break;
   150                 }
   151                 methodSpec = methodSpec + token.toString();
   152             }
   153         }
   154         return methodSpec.trim();
   155     }
   156 
   157     public static PlsqlBlock findMethod(final List<PlsqlBlock> specBlockHier, final String packageName, final String methodName) {
   158         PlsqlBlock match = null;
   159         if (!packageName.equals("")) {
   160             PlsqlBlock packageBlock = getPackageBody(specBlockHier, PlsqlBlockType.PACKAGE_BODY, packageName);
   161             if (packageBlock != null) {
   162                 for (int i = 0; i < packageBlock.getChildCount(); i++) {
   163                     final PlsqlBlock temp = packageBlock.getChildBlocks().get(i);
   164                     if (temp.getName().equals(methodName)) {
   165                         match = temp;
   166                         break;
   167                     }
   168                 }
   169             }
   170         }
   171         return match;
   172     }
   173 
   174     public static PlsqlBlock getPackageBody(final List<PlsqlBlock> specBlockHier, final PlsqlBlockType blockType, final String packageName) {
   175         PlsqlBlock packageBlock = null;
   176         for (int i = 0; i < specBlockHier.size(); i++) {
   177             final PlsqlBlock temp = specBlockHier.get(i);
   178             if (temp.getType() == blockType && temp.getName().equalsIgnoreCase(packageName)) {
   179                 packageBlock = temp;
   180                 break;
   181             }
   182         }
   183         return packageBlock;
   184     }
   185 
   186     public static int getOffsetToInsert(final Document doc, final List<PlsqlBlock> specBlockHier, final String packageName, final PlsqlBlock searchBlock, final int searchPlace) throws BadLocationException {
   187         int offset = -1;
   188         //Get package block
   189         if (!packageName.equals("")) {
   190             PlsqlBlock packageBlock = getPackageBody(specBlockHier, PlsqlBlockType.PACKAGE, packageName);
   191 
   192             if (packageBlock != null) {
   193                 for (int i = 0; i < packageBlock.getChildCount(); i++) {
   194                     final PlsqlBlock temp = packageBlock.getChildBlocks().get(i);
   195                     if (!temp.getType().equals(PlsqlBlockType.COMMENT) && (temp.getType().equals(PlsqlBlockType.FUNCTION_DEF) || temp.getType().equals(PlsqlBlockType.PROCEDURE_DEF))) {
   196                         if (temp.getName().contains(searchBlock.getName())) {
   197                             if (searchPlace == -1) {
   198                                 offset = temp.getStartOffset();
   199                             } else {
   200                                 offset = packageBlock.getChildBlocks().get(i + 1).getStartOffset() - 1;
   201                             }
   202                             break;
   203                         }
   204                     } else if (temp.getType().equals(PlsqlBlockType.COMMENT) && searchBlock.getType().equals(PlsqlBlockType.COMMENT)) {
   205 
   206                         //Get block content and check; comments can be merged to one comment block
   207                         final String text = doc.getText(temp.getStartOffset(), temp.getEndOffset() - temp.getStartOffset());
   208                         int index = text.indexOf(searchBlock.getName());
   209                         if (index != -1) {
   210                             index = text.indexOf("\n", index);
   211                             if (index != -1) {
   212                                 index = text.indexOf("\n", index + 1);
   213                                 if (index != -1) {
   214                                     offset = temp.getStartOffset() + index;
   215                                 } else {
   216                                     offset = temp.getEndOffset();
   217                                 }
   218 
   219                                 break;
   220                             }
   221                         }
   222                     }
   223                 }
   224 
   225                 //If the comment is not found insert some where
   226                 if (offset == -1) {
   227                     offset = packageBlock.getChildBlocks().get(0).getEndOffset();
   228                 }
   229             }
   230         }
   231 
   232         return offset;
   233     }
   234 
   235     public static boolean changeParam(final Document doc, final int offset, final String methodName) {
   236         if (PlsqlAnnotationUtil.isFileReadOnly(doc)) {
   237             JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), "File is read-only", "Error", JOptionPane.ERROR_MESSAGE);
   238             return false;
   239         }
   240 
   241         final TokenHierarchy tokenHierarchy = TokenHierarchy.get(doc);
   242         @SuppressWarnings("unchecked")
   243         final TokenSequence<PlsqlTokenId> ts = tokenHierarchy.tokenSequence(PlsqlTokenId.language());
   244 
   245         if (ts != null) {
   246             ts.move(offset);
   247             ts.movePrevious();
   248             Token<PlsqlTokenId> token = ts.token();
   249             while (token.id() == PlsqlTokenId.WHITESPACE && ts.movePrevious()) {
   250                 token = ts.token();
   251             }
   252             //We have the token now
   253             if (PlsqlFileAnnotationUtil.changeLineOfOffset(doc, ts.offset(), token.toString(), "'" + methodName + "'")) {
   254                 return true;
   255             }
   256         }
   257         return false;
   258     }
   259 
   260     public static int isReturnExist(final Document doc, final PlsqlBlock block) {
   261         hasReturn = 0;
   262         boolean isMissing = isReturn(doc, block);
   263 
   264         if (!isMissing) {
   265             if (hasReturn == HAS_RETURNS) {
   266                 return HAS_RETURNS;
   267             } else {
   268                 return NO_RETURNS;
   269             }
   270         } else {
   271             return 0;
   272         }
   273     }
   274 
   275     public static boolean isReturn(final Document doc, final PlsqlBlock block) {
   276         boolean isReturn = false;
   277         final List<PlsqlBlock> children = block.getChildBlocks();
   278         Collections.sort(children, comparator);
   279 
   280         int startOffset = findBlockImplStart(doc, block);
   281         for (PlsqlBlock child : children) {
   282             if (child.getType() != PlsqlBlockType.CURSOR
   283                     && child.getType() != PlsqlBlockType.CUSTOM_FOLD
   284                     && startOffset < child.getStartOffset()) {
   285                 if (!isReturnMissing(startOffset, child.getStartOffset(), doc, block, true)) {
   286                     isReturn = true;
   287                     break;
   288                 }
   289             }
   290             startOffset = child.getEndOffset();
   291         }
   292 
   293         if (!isReturn) {
   294             isReturn = checkReturnInChildren(children, doc);
   295         }
   296 
   297         //Return not complete in child blocks, check for default return at the end
   298         //check for exception block, it is assumed that there will be no child blocks after EXCEPTION
   299         if (children.size() > 0) {
   300             startOffset = children.get(children.size() - 1).getEndOffset();
   301         }
   302 
   303         isReturn = !isReturnMissing(startOffset, block.getEndOffset(), doc, block, !isReturn);
   304 
   305         return isReturn;
   306     }
   307 
   308     private static int checkExceptionBlock(final Document doc, final PlsqlBlock block) {
   309 
   310         int startOffset = findBlockImplStart(doc, block);
   311         final TokenHierarchy tokenHierarchy = TokenHierarchy.get(doc);
   312         @SuppressWarnings("unchecked")
   313         final TokenSequence<PlsqlTokenId> ts = tokenHierarchy.tokenSequence(PlsqlTokenId.language());
   314 
   315         if (ts != null) {
   316             ts.move(startOffset);
   317             Token<PlsqlTokenId> token = ts.token();
   318             while (ts.moveNext() && ts.offset() < block.getEndOffset()) {
   319                 token = ts.token();
   320                 if (token.toString().equalsIgnoreCase("EXCEPTION")) {
   321                     return ts.offset();
   322                 }
   323             }
   324         }
   325         return 0;
   326     }
   327 
   328     private static boolean checkReturnInChildren(final List<PlsqlBlock> children, final Document doc) {
   329         boolean isConstrusctReturn = false;
   330 
   331         for (PlsqlBlock child : children) {
   332             if ((child.getType() == PlsqlBlockType.IF && child.getName().toUpperCase(Locale.ENGLISH).startsWith("IF"))
   333                     || (child.getType() == PlsqlBlockType.CASE && child.getName().toUpperCase(Locale.ENGLISH).startsWith("CASE"))) {
   334                 isConstrusctReturn = false;
   335                 isConstrusctReturn = isReturn(doc, child);
   336             } else if ((child.getType() == PlsqlBlockType.IF && child.getName().toUpperCase(Locale.ENGLISH).startsWith("ELSIF"))
   337                     || (child.getType() == PlsqlBlockType.IF && child.getName().toUpperCase(Locale.ENGLISH).startsWith("WHEN"))) {
   338                 if (isConstrusctReturn) {
   339                     isConstrusctReturn = isReturn(doc, child);
   340                 }
   341             } else if ((child.getType() == PlsqlBlockType.IF || child.getType() == PlsqlBlockType.CASE)
   342                     && child.getName().equalsIgnoreCase("ELSE")) {
   343                 if (isConstrusctReturn) {
   344                     isConstrusctReturn = isReturn(doc, child);
   345                     //If is returning and else if also returning
   346                     if (isConstrusctReturn) {
   347                         return true;
   348                     }
   349                 }
   350             }
   351         }
   352 
   353         return false;
   354     }
   355 
   356     private static boolean isReturnMissing(final int startOffset, final int endOffset, final Document doc, PlsqlBlock block, boolean isReturnMissing) {
   357         final TokenHierarchy tokenHierarchy = TokenHierarchy.get(doc);
   358         @SuppressWarnings("unchecked")
   359         final TokenSequence<PlsqlTokenId> ts = tokenHierarchy.tokenSequence(PlsqlTokenId.language());
   360 
   361         if (ts != null) {
   362             ts.move(startOffset);
   363             Token<PlsqlTokenId> token = ts.token();
   364             boolean isException = false;
   365 
   366             while (ts.moveNext() && ts.offset() < endOffset) {
   367                 token = ts.token();
   368 
   369                 if (token.toString().equalsIgnoreCase("RETURN")
   370                         || token.toString().equalsIgnoreCase("RAISE")) {
   371                     if (moveToReturnEnd(ts, endOffset, doc, block)) {
   372                         isReturnMissing = false;
   373                         hasReturn = HAS_RETURNS;
   374                     }
   375                 } else if (token.toString().equalsIgnoreCase("ERROR_SYS")
   376                         || token.toString().equalsIgnoreCase("APPLICATION_SEARCH_SYS")
   377                         || token.toString().equalsIgnoreCase("VMO_ERROR_SYS")) {
   378                     if (ts.moveNext()) {
   379                         token = ts.token();
   380                         if (token.id() == PlsqlTokenId.DOT) {
   381                             if (ts.moveNext()) {
   382                                 token = ts.token();
   383                                 if (!token.toString().toLowerCase(Locale.ENGLISH).startsWith("check")) {
   384                                     if (moveToReturnEnd(ts, endOffset, doc, block)) {
   385                                         isReturnMissing = false;
   386                                         hasReturn = HAS_RETURNS;
   387                                     }
   388                                 }
   389                             }
   390                         }
   391                     }
   392                 } else if (token.toString().equalsIgnoreCase("EXCEPTION")) {
   393                     isException = true;
   394                 } else if (token.toString().equalsIgnoreCase("WHEN") && isException) {
   395                     if (!isReturnMissing) {
   396                         if (PlsqlParserUtil.getNextNonWhitespace(ts, true)) { //Exception name
   397                             if (PlsqlParserUtil.getNextNonWhitespace(ts, true)) {
   398                                 token = ts.token();
   399                                 if (token.toString().equalsIgnoreCase("THEN")) {
   400                                     isReturnMissing = true;
   401                                 }
   402                             }
   403                         }
   404                     } else {
   405                         //return missing in above WHEN
   406                         break;
   407                     }
   408                 }
   409             }
   410         }
   411 
   412         return isReturnMissing;
   413     }
   414 
   415     public static boolean getUnreachableOffsets(final Document doc, final PlsqlBlock block, final List<Integer> lstUnreachable) throws BadLocationException {
   416         final List<PlsqlBlock> children = block.getChildBlocks();
   417         Collections.sort(children, comparator);
   418         boolean isConstrusctReturn = false;
   419         boolean isChildReturn = false;
   420         boolean isReturn = false;
   421         boolean isException = false;
   422         int startOffset = findBlockImplStart(doc, block);
   423         for (PlsqlBlock child : children) {
   424             if (child.getType() != PlsqlBlockType.CURSOR
   425                     && child.getType() != PlsqlBlockType.CUSTOM_FOLD && child.getType() != PlsqlBlockType.COMMENT) {
   426                 if (startOffset < child.getStartOffset()) {
   427                     if (getUnreachableOffsets(startOffset, child.getStartOffset(), doc, block, lstUnreachable, isReturn, isException)) {
   428                         isReturn = true;
   429                     } else {
   430                         isReturn = false;
   431                     }
   432                 }
   433 
   434                 isChildReturn = getUnreachableOffsets(doc, child, lstUnreachable);
   435 
   436                 if ((child.getType() == PlsqlBlockType.IF && child.getName().toUpperCase(Locale.ENGLISH).startsWith("IF"))
   437                         || (child.getType() == PlsqlBlockType.CASE && child.getName().toUpperCase(Locale.ENGLISH).startsWith("CASE"))) {
   438                     isConstrusctReturn = isChildReturn;
   439                 } else if ((child.getType() == PlsqlBlockType.IF && child.getName().toUpperCase(Locale.ENGLISH).startsWith("ELSIF"))
   440                         || (child.getType() == PlsqlBlockType.IF && child.getName().toUpperCase(Locale.ENGLISH).startsWith("WHEN"))) {
   441                     if (isConstrusctReturn) {
   442                         isConstrusctReturn = isChildReturn;
   443                     }
   444                 } else if ((child.getType() == PlsqlBlockType.IF || child.getType() == PlsqlBlockType.CASE)
   445                         && child.getName().equalsIgnoreCase("ELSE")) {
   446                     if (isConstrusctReturn) {
   447                         isConstrusctReturn = isChildReturn;
   448                         if (isConstrusctReturn) {
   449                             isReturn = true;
   450                         }
   451                     }
   452                 }
   453             }
   454             startOffset = child.getEndOffset();
   455         }
   456         if (checkExceptionBlock(doc, block) < startOffset) {
   457             isException = true;
   458         }
   459         isConstrusctReturn = getUnreachableOffsets(startOffset, block.getEndOffset(), doc, block, lstUnreachable, isReturn, isException);
   460         if (!isReturn) {
   461             isReturn = isConstrusctReturn;
   462         }
   463 
   464         return isReturn;
   465     }
   466 
   467     private static boolean getUnreachableOffsets(final int startOffset, final int endOffset, final Document doc, PlsqlBlock block, final List<Integer> lstUnreachable, boolean isReturn, boolean isException) throws BadLocationException {
   468         final TokenHierarchy tokenHierarchy = TokenHierarchy.get(doc);
   469         @SuppressWarnings("unchecked")
   470         final TokenSequence<PlsqlTokenId> ts = tokenHierarchy.tokenSequence(PlsqlTokenId.language());
   471         int endLineCount = 0;
   472         String previous = null;
   473         if (ts != null) {
   474             ts.move(startOffset);
   475             Token<PlsqlTokenId> token = ts.token();
   476             boolean isRaised = false;
   477 
   478             while (ts.moveNext() && ts.offset() < endOffset) {
   479                 token = ts.token();
   480 
   481                 if (token.toString().equalsIgnoreCase("RETURN")) {
   482                     if(isReturn && endLineCount > 0){
   483                         lstUnreachable.add(ts.offset());
   484                         endLineCount = 0;
   485                     }
   486                     else if (moveToReturnEnd(ts, endOffset, doc, block)) {
   487                         isReturn = true;
   488                     }
   489                 } else if (token.toString().equalsIgnoreCase("RAISE")) {
   490                     if (moveToReturnEnd(ts, endOffset, doc, block)) {
   491                         isRaised = true;
   492                     }
   493                 } else if (token.toString().equalsIgnoreCase("EXCEPTION")) {
   494                     isException = true;
   495                     isReturn = false;
   496                     isRaised = false;
   497                 } else if (token.toString().equalsIgnoreCase("WHEN") && isException) {
   498                     isReturn = false;
   499                     isRaised = false;
   500                     if (PlsqlParserUtil.getNextNonWhitespace(ts, true)) { //Exception name
   501                         if (PlsqlParserUtil.getNextNonWhitespace(ts, true)) {
   502                             token = ts.token();
   503                             if (token.toString().equalsIgnoreCase("THEN") || token.toString().equalsIgnoreCase("OR")) {
   504                                 isReturn = false;
   505                                 isRaised = false;
   506                                 endLineCount = 0;
   507                             }
   508                         }
   509                     }
   510                 } else if ((isReturn || isRaised) && token.toString().contains("\n")) {
   511                     endLineCount++;
   512                 } else if (endLineCount > 0 && token.toString().toUpperCase(Locale.ENGLISH).contains("END") || isRaised ) {
   513                     endLineCount = 0;
   514                 } else if (endLineCount > 0 && token.toString().contains(";")) {
   515                     lstUnreachable.add(ts.offset());
   516                 }
   517 
   518                 if (token.toString().contains(";") && (previous != null && previous.equals("END"))) {
   519                     isException = false;
   520                     isReturn = false;
   521                     isRaised = false;
   522                 }
   523 
   524                 previous = token.toString();
   525             }
   526         }
   527         return isReturn;
   528     }
   529 
   530     private static int findBlockImplStart(final Document doc, final PlsqlBlock block) {
   531         int startOffset = block.getStartOffset();
   532         final TokenHierarchy tokenHierarchy = TokenHierarchy.get(doc);
   533         @SuppressWarnings("unchecked")
   534         final TokenSequence<PlsqlTokenId> ts = tokenHierarchy.tokenSequence(PlsqlTokenId.language());
   535 
   536         if (ts != null) {
   537             ts.move(startOffset);
   538             Token<PlsqlTokenId> token = ts.token();
   539             while (ts.moveNext() && ts.offset() < block.getEndOffset()) {
   540                 token = ts.token();
   541 
   542                 if (token.toString().equalsIgnoreCase("BEGIN")) {
   543                     startOffset = ts.offset();
   544                     break;
   545                 }
   546             }
   547         }
   548 
   549         return startOffset;
   550     }
   551 
   552     private static boolean moveToReturnEnd(final TokenSequence<PlsqlTokenId> ts, final int endOffset, final Document doc, final PlsqlBlock block) {
   553         Token<PlsqlTokenId> token = ts.token();
   554              while (ts.moveNext() && ts.offset() <  block.getEndOffset()) {
   555             token = ts.token();
   556             
   557             // to handle scenarios like Return CASE 'a' THEN 'A'
   558             if (token.toString().equalsIgnoreCase("CASE") || token.toString().equalsIgnoreCase("IF")) {
   559                 final TokenHierarchy tokenHierarchy = TokenHierarchy.get(doc);
   560                 int startOffset = token.offset(tokenHierarchy);
   561                 final TokenSequence<PlsqlTokenId> tsChild = tokenHierarchy.tokenSequence(PlsqlTokenId.language());
   562                 boolean caseReturned = false;
   563                 if (tsChild != null) {
   564                     tsChild.move(startOffset);
   565                     Token<PlsqlTokenId> tokenChild = tsChild.token();
   566                     while (tsChild.moveNext() && tsChild.offset() < block.getEndOffset()) {
   567                         tokenChild = tsChild.token();
   568 
   569                         if (tokenChild.toString().equalsIgnoreCase("ELSE") || tokenChild.toString().equalsIgnoreCase("DEFAULT")) {
   570                             caseReturned = true;
   571                         }
   572 
   573                         if (tokenChild.toString().equals(";")) {
   574                             ts.move(tokenChild.offset(tokenHierarchy));
   575                             return caseReturned;
   576                         }
   577                     }
   578                 }
   579             }
   580             else if (token.toString().equals(";")) {
   581                 return true;
   582             }
   583         }
   584 
   585         return false;
   586     }
   587 
   588     public static PlsqlBlock findMatchingImpl(final List<PlsqlBlock> blockHier, final Document source,
   589             final Document dest, final PlsqlBlock sourceBlock) {
   590         PlsqlBlock match = null;
   591         final List<PlsqlBlock> matchList = new ArrayList<PlsqlBlock>();
   592         PlsqlParserUtil.findMatchingImplBlocks(blockHier, sourceBlock.getName(), matchList);
   593 
   594         //There are several methods with the same name. Have to check the signature
   595         final List<String> usageParams = PlsqlParserUtil.fetchMethodDefParamTypes(source, sourceBlock.getStartOffset());
   596 
   597         //Take PlsqlBlock one by one and compare the parameters
   598         for (int x = 0; x < matchList.size(); x++) {
   599             final PlsqlBlock block = matchList.get(x);
   600             if (!(block.getParent() != null && sourceBlock.getParent() != null ? block.getParent().getName().equals(sourceBlock.getParent().getName()) : true)) {
   601                 continue; //If the parent block name is not the same this is not a match
   602             }
   603             final List<String> params = PlsqlParserUtil.fetchMethodDefParamTypes(dest, block.getStartOffset());
   604             final int defaultNo = PlsqlParserUtil.fetchDefaultParams(dest, block.getStartOffset());
   605             if (PlsqlParserUtil.compareParameters(usageParams, params, defaultNo)) {
   606                 match = block;
   607                 break;
   608             }
   609         }
   610 
   611         return match;
   612     }
   613 }