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