PLSQL/Lexer/src/org/netbeans/modules/plsql/lexer/PlsqlBlockFactory.java
author Subhashini Sooriarachchi <subslk@netbeans.org>
Mon, 12 Aug 2013 11:26:54 +0530
changeset 464 e10b2e8563fc
parent 405 f949ce7a3fb1
permissions -rw-r--r--
EADS-3749 encountering issues with the displaying of code in Developer Studio when code folding is enabled
     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.lexer;
    43 
    44 import java.util.ArrayList;
    45 import java.util.Collections;
    46 import java.util.HashMap;
    47 import java.util.HashSet;
    48 import java.util.LinkedList;
    49 import java.util.List;
    50 import java.util.Locale;
    51 import java.util.Map;
    52 import java.util.Observable;
    53 import java.util.StringTokenizer;
    54 import java.util.logging.Level;
    55 import java.util.logging.Logger;
    56 import javax.swing.SwingUtilities;
    57 import javax.swing.event.DocumentEvent;
    58 import javax.swing.event.DocumentListener;
    59 import javax.swing.text.AbstractDocument;
    60 import javax.swing.text.BadLocationException;
    61 import javax.swing.text.Document;
    62 import org.netbeans.api.lexer.Token;
    63 import org.netbeans.api.lexer.TokenHierarchy;
    64 import org.netbeans.api.lexer.TokenSequence;
    65 import org.openide.ErrorManager;
    66 import org.openide.util.RequestProcessor;
    67 
    68 /**
    69  * Class that will maintain code blocks of the file.
    70  *
    71  * @author YaDhLK
    72  */
    73 public class PlsqlBlockFactory extends Observable implements DocumentListener {
    74 
    75     private static final Logger LOG = Logger.getLogger(PlsqlBlockFactory.class.getName());
    76     private List<PlsqlBlock> blockHierarchy;
    77     private List<PlsqlBlock> customFoldBlocks;
    78     private List<PlsqlBlock> newBlocks;
    79     private List<PlsqlBlock> toBeRemoved;
    80     private List<PlsqlBlock> changedBlocks;
    81     private HashSet<Integer> unsuccessBlocks;
    82     private HashMap<String, String> definesMap;
    83     private TokenHierarchy tokenHierarchy;
    84     private int docStartOffset = 0;
    85     private int docEndOffset = 0;
    86     private int startParse = 0;
    87     private int endParse = 0;
    88     private int changedLength = 0;
    89     private boolean isDefineChanged = false;
    90     private static RequestProcessor.Task updateBlocksTask = null;
    91     private static final LinkedList<EventProperties> updateEvents = new LinkedList<EventProperties>();
    92     private static final int DEFAULT_WAIT_TIME = 500;
    93     private boolean saveInProgress = false;
    94     private boolean caseChangeInProgress = false;
    95     private static final String updateLock = "This is used to synchronize navigator updates";
    96     private static final RequestProcessor RP = new RequestProcessor(PlsqlBlockFactory.class);
    97 
    98     public PlsqlBlockFactory() {
    99         blockHierarchy = new ArrayList<PlsqlBlock>();
   100         tokenHierarchy = null;
   101         newBlocks = new ArrayList<PlsqlBlock>();
   102         toBeRemoved = new ArrayList<PlsqlBlock>();
   103         changedBlocks = new ArrayList<PlsqlBlock>();
   104         customFoldBlocks = new ArrayList<PlsqlBlock>();
   105         definesMap = new HashMap<String, String>();
   106         unsuccessBlocks = new HashSet<Integer>();
   107     }
   108 
   109     /**
   110      * Method used to reparse the whole document
   111      *
   112      * @param doc
   113      */
   114     public void reParse(Document doc) {
   115         clear();
   116 
   117         if (doc == null) {
   118             return;
   119         }
   120 
   121         docStartOffset = doc.getStartPosition().getOffset();
   122         docEndOffset = doc.getEndPosition().getOffset();
   123         startParse = docStartOffset;
   124         endParse = docEndOffset - 1;
   125 
   126         //clean block hierarchy
   127         blockHierarchy.clear();
   128         customFoldBlocks.clear();
   129         generateBlocks(doc);
   130     }
   131 
   132     /**
   133      * Return new blocks that were recognized by the latest change
   134      *
   135      * @return
   136      */
   137     public List<PlsqlBlock> getNewBlocks() {
   138         return newBlocks;
   139     }
   140 
   141     /**
   142      * Return the blocks who's offsets have changed
   143      *
   144      * @return
   145      */
   146     public List<PlsqlBlock> getChangedBlocks() {
   147         return changedBlocks;
   148     }
   149 
   150     /**
   151      * Return the custom fold blocks that are there
   152      *
   153      * @return
   154      */
   155     public List<PlsqlBlock> getCustomFolds() {
   156         return customFoldBlocks;
   157     }
   158 
   159     /**
   160      * Return block hierarchy
   161      *
   162      * @return
   163      */
   164     public List<PlsqlBlock> getBlockHierarchy() {
   165         return blockHierarchy;
   166     }
   167 
   168     /**
   169      * Method that will return the blocks that are removed
   170      *
   171      * @return
   172      */
   173     public List<PlsqlBlock> getRemovedBlocks() {
   174         return toBeRemoved;
   175     }
   176 
   177     /**
   178      * Check whether there are childrean of this fold here, if so add them
   179      *
   180      * @param block
   181      * @param immediateBlockHier
   182      */
   183     private void addImmediateChildren(PlsqlBlock block, List<PlsqlBlock> immediateBlockHier) {
   184         for (int i = immediateBlockHier.size() - 1; i >= 0; i--) {
   185             PlsqlBlock tmp = immediateBlockHier.get(i);
   186             if ((tmp.getStartOffset() > block.getStartOffset())
   187                     && (tmp.getEndOffset() < block.getEndOffset())) {
   188                 if (checkExisting(tmp, newBlocks)) {
   189                     newBlocks.remove(tmp);
   190                 } else {
   191                     removeBlock(block, immediateBlockHier);
   192                 }
   193 
   194                 block.addChild(tmp);
   195             }
   196         }
   197     }
   198 
   199     /**
   200      * Method that will look for custom start or end token based on the given type
   201      *
   202      * @param customEndToken
   203      * @param ts
   204      * @param immediateBlockHier
   205      * @param parent
   206      * @param type
   207      */
   208     private void checkCustom(Token<PlsqlTokenId> customToken, TokenSequence<PlsqlTokenId> ts, List<PlsqlBlock> immediateBlockHier, PlsqlBlock parent, String type) {
   209         Token<PlsqlTokenId> found = customToken;
   210         Token<PlsqlTokenId> token = customToken;
   211 
   212         if (type.equals("START")) {
   213             //We have to find the end fold token now
   214             ts.move(found.offset(tokenHierarchy));
   215             ts.moveNext();
   216             while (ts.moveNext()) {
   217                 token = ts.token();
   218                 String image = token.text().toString();
   219                 PlsqlTokenId tokenID = token.id();
   220 
   221                 if (tokenID == PlsqlTokenId.LINE_COMMENT) {
   222                     //only single comment line
   223                     if (image.toUpperCase(Locale.ENGLISH).contains("<FOLD>")) {
   224                         //We have come to another start
   225                         return;
   226                     } else if (image.toUpperCase(Locale.ENGLISH).contains("<END-FOLD>")) {
   227                         if (isTokenOk(token, immediateBlockHier, parent)) {
   228                             String name = found.text().toString();
   229                             int index = name.toUpperCase(Locale.ENGLISH).indexOf("<FOLD>");
   230                             name = name.substring(index + 7).trim();
   231                             if (ts.moveNext()) {
   232                                 token = ts.token();
   233                                 PlsqlBlock custom = new PlsqlBlock(found.offset(tokenHierarchy),
   234                                         token.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
   235                                 customFoldBlocks.add(custom);
   236                             }
   237                         }
   238                         //since we have found the other tag return
   239                         return;
   240                     }
   241                 }
   242             }
   243         } else {
   244             ts.move(found.offset(tokenHierarchy));
   245             //We have to find the start fold token now
   246             while (ts.movePrevious()) {
   247                 token = ts.token();
   248                 String image = token.text().toString();
   249                 PlsqlTokenId tokenID = token.id();
   250 
   251                 if (tokenID == PlsqlTokenId.LINE_COMMENT) {
   252                     //only single comment line
   253                     if (image.toUpperCase(Locale.ENGLISH).contains("<FOLD>")) {
   254                         if (isTokenOk(token, immediateBlockHier, parent)) {
   255                             String name = image;
   256                             int index = name.toUpperCase(Locale.ENGLISH).indexOf("<FOLD>");
   257                             name = name.substring(index + 7).trim();
   258                             ts.move(found.offset(tokenHierarchy));
   259                             ts.moveNext();
   260                             if (ts.moveNext()) {
   261                                 found = ts.token();
   262                                 PlsqlBlock custom = new PlsqlBlock(token.offset(tokenHierarchy),
   263                                         found.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
   264                                 customFoldBlocks.add(custom);
   265                             }
   266                         }
   267                         //since we have found the other tag return
   268                         return;
   269                     } else if (image.toUpperCase(Locale.ENGLISH).contains("<END-FOLD>")) {
   270                         //We have come to another end
   271                         return;
   272                     }
   273                 }
   274             }
   275         }
   276     }
   277 
   278     /**
   279      * Method that will check for a Java Source block
   280      *
   281      * @param tempToken
   282      * @param ts
   283      * @param immediateBlockHier
   284      * @return
   285      */
   286     private PlsqlBlock checkJavaSource(Token<PlsqlTokenId> tempToken, TokenSequence<PlsqlTokenId> ts) {
   287         Token<PlsqlTokenId> begin = tempToken;
   288         Token<PlsqlTokenId> tmp = tempToken;
   289         PlsqlBlock block = null;
   290 
   291         //Check whether the beginning is in a SQL Plus command
   292         if (sqlPlusLine(ts)) {
   293             return null;
   294         }
   295 
   296         while (ts.moveNext()) {
   297             tmp = ts.token();
   298             String image = tmp.text().toString();
   299             PlsqlTokenId tokenID = tmp.id();
   300 
   301             if ((!image.equals("/")) && (tmp.offset(tokenHierarchy) > endParse)) {
   302                 break;
   303             }
   304 
   305             //We might have come to the end of the procedure/function declaration
   306             if ((tokenID == PlsqlTokenId.OPERATOR) && image.equals("/")) {
   307                 //check whether previous Non white space token to the identifier is END
   308                 int offset = ts.offset();
   309                 if (checkForOnlyChar(ts, offset)) {
   310                     ts.move(offset);
   311                     ts.moveNext();
   312                     ts.moveNext();
   313                     block = new PlsqlBlock(begin.offset(tokenHierarchy), offset, "", "", PlsqlBlockType.JAVA_SOURCE);
   314                     checkPrefix(begin.offset(tokenHierarchy), ts, block);
   315                     break;
   316                 }
   317             }
   318         }
   319 
   320         return block;
   321     }
   322 
   323     /**
   324      * Check whether this current token is the only token in this line
   325      *
   326      * @param ts
   327      * @param offset
   328      * @return
   329      */
   330     private boolean checkForOnlyChar(TokenSequence<PlsqlTokenId> ts, int offset) {
   331         boolean isStartOk = true;
   332         boolean isEndOk = true;
   333         ts.move(offset);
   334         Token<PlsqlTokenId> token = null;
   335         while (ts.movePrevious()) {
   336             token = ts.token();
   337             if (token.id() == PlsqlTokenId.WHITESPACE) {
   338                 if (token.toString().contains("\n")) {
   339                     break;
   340                 }
   341             } else if (token.id() == PlsqlTokenId.JAVA_SOUCE) {
   342                 break;
   343             } else {
   344                 isStartOk = false;
   345                 break;
   346             }
   347         }
   348 
   349         ts.move(offset);
   350         ts.moveNext(); //current token
   351         while (ts.moveNext()) {
   352             token = ts.token();
   353             if (token.id() == PlsqlTokenId.WHITESPACE) {
   354                 if (token.toString().contains("\n")) {
   355                     break;
   356                 }
   357             } else {
   358                 isEndOk = false;
   359                 break;
   360             }
   361         }
   362 
   363         ts.move(offset);
   364         ts.moveNext();
   365 
   366         if (isStartOk && isEndOk) {
   367             return true;
   368         }
   369 
   370         return false;
   371     }
   372 
   373     /**
   374      * Method that will check for statement blocks other than the CURSOR and VIEW
   375      *
   376      * @param tempToken
   377      * @param ts
   378      * @param immediateBlockHier
   379      * @return
   380      */
   381     private PlsqlBlock checkStatementBlock(Token<PlsqlTokenId> current, TokenSequence<PlsqlTokenId> ts, List<PlsqlBlock> parentBlocks) {
   382         List<PlsqlBlock> lstChild = new ArrayList<PlsqlBlock>();
   383         PlsqlBlock block = null;
   384 
   385         //Check whether the beginning is in a SQL Plus command
   386         if (sqlPlusLine(ts)) {
   387             return null;
   388         }
   389 
   390         boolean moveNext = ts.moveNext();
   391         Token<PlsqlTokenId> token = ts.token();
   392         Token<PlsqlTokenId> stmtBegin = current;
   393         boolean getName = true;
   394         Token<PlsqlTokenId> customStartToken = null;
   395         String name = current.text().toString();
   396 
   397         while (moveNext) {
   398             String image = token.text().toString();
   399             PlsqlTokenId tokenID = token.id();
   400 
   401             if ((token != null) && (!image.equals(";")) && (!image.equals("/")) && (token.offset(tokenHierarchy) > endParse)) {
   402                 break;
   403             }
   404 
   405             if (image.equals(";") || (image.equals("/") && checkForOnlyChar(ts, ts.offset()))) {
   406                 block = new PlsqlBlock(stmtBegin.offset(tokenHierarchy), token.offset(tokenHierarchy), name.trim(), "", PlsqlBlockType.STATEMENT);
   407                 checkPrefix(stmtBegin.offset(tokenHierarchy), ts, block);
   408                 break;
   409             } else if (image.equalsIgnoreCase("CREATE")
   410                     || image.equalsIgnoreCase("DECLARE")
   411                     || image.equalsIgnoreCase("BEGIN")
   412                     || image.equalsIgnoreCase("WHEN")
   413                     || image.equalsIgnoreCase("THEN")
   414                     || image.equalsIgnoreCase("IF")
   415                     || image.equalsIgnoreCase("END")
   416                     || image.equalsIgnoreCase("ELSE")
   417                     || image.equalsIgnoreCase("LOOP")) {
   418                 break;
   419             } else if (image.equalsIgnoreCase("CASE")) {
   420                 int beforeOff = token.offset(tokenHierarchy);
   421                 List children = checkCaseBlock(token, ts, lstChild, true);
   422                 if (children == null || children.isEmpty()) {//If inner check seems to have failed need to continue this one
   423 
   424                     ts.move(beforeOff);
   425                     moveNext = ts.moveNext();
   426                 } else {
   427                     for (int i = 0; i < children.size(); i++) {
   428                         PlsqlBlock child = (PlsqlBlock) children.get(i);
   429                         if (checkExisting(child, lstChild) == false) {
   430                             lstChild.add(child);
   431                         }
   432                     }
   433                 }
   434             } else if (tokenID == PlsqlTokenId.LINE_COMMENT) {
   435                 //only single comment line
   436                 if (image.toUpperCase(Locale.ENGLISH).contains("<FOLD>")) {
   437                     customStartToken = token;
   438                 } else if (image.toUpperCase(Locale.ENGLISH).contains("<END-FOLD>")) {
   439                     if (customStartToken != null) {
   440                         if (ts.moveNext()) {
   441                             token = ts.token();
   442                             PlsqlBlock custom = new PlsqlBlock(customStartToken.offset(tokenHierarchy),
   443                                     token.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
   444                             customFoldBlocks.add(custom);
   445                         }
   446                         customStartToken = null;
   447                     }
   448                 } else {
   449                     PlsqlBlock child = checkComment(token, ts);
   450                     if (child != null) {
   451                         if (checkExisting(child, lstChild) == false) {
   452                             lstChild.add(child);
   453                         }
   454                     }
   455                 }
   456             } else if (tokenID == PlsqlTokenId.BLOCK_COMMENT) {
   457                 int start = token.offset(tokenHierarchy);
   458                 PlsqlBlock child = new PlsqlBlock(start,
   459                         start + token.length(), "BLOCK COMMENT", "", PlsqlBlockType.COMMENT);
   460                 if (child != null) {
   461                     if (checkExisting(child, lstChild) == false) {
   462                         lstChild.add(child);
   463                     }
   464                 }
   465             } else if (tokenID == PlsqlTokenId.WHITESPACE && image.contains("\n")) {
   466                 getName = false;
   467             } else if (getName) {
   468                 name = name + image;
   469             }
   470 
   471             moveNext = ts.moveNext();
   472             token = ts.token();
   473         }
   474 
   475         if (block != null) {
   476             //add children
   477             addChildren(block, lstChild, parentBlocks);
   478         }
   479 
   480         return block;
   481     }
   482 
   483     /**
   484      * Check whether the given token offset is included in any existing block
   485      *
   486      * @param token
   487      * @param immediateBlockHier
   488      * @param parent
   489      * @return
   490      */
   491     private boolean isTokenOk(Token<PlsqlTokenId> token, List<PlsqlBlock> immediateBlockHier, PlsqlBlock parent) {
   492         boolean isOk = true;
   493         int offset = token.offset(tokenHierarchy);
   494         for (int i = immediateBlockHier.size() - 1; i >= 0; i--) {
   495             PlsqlBlock block = immediateBlockHier.get(i);
   496             if ((block.getStartOffset() <= offset) && (block.getEndOffset() >= offset)) {
   497                 isOk = false;
   498                 break;
   499             }
   500         }
   501 
   502         if (isOk && parent != null) {
   503             if (!((parent.getStartOffset() <= offset) && (parent.getEndOffset() >= offset))) {
   504                 isOk = false;
   505             }
   506         }
   507         return isOk;
   508     }
   509 
   510     /**
   511      * Method that will look for trigger blocks
   512      *
   513      * @param tempToken
   514      * @param ts
   515      * @param parentBlocks
   516      * @return
   517      */
   518     private PlsqlBlock checkTrigger(Token<PlsqlTokenId> tiggerToken, TokenSequence<PlsqlTokenId> ts, List<PlsqlBlock> parentBlocks) {
   519         Token<PlsqlTokenId> triggerBegin = tiggerToken;
   520         Token<PlsqlTokenId> tmp = tiggerToken;
   521         PlsqlBlock block = null;
   522         List<PlsqlBlock> lstChild = new ArrayList<PlsqlBlock>();
   523 
   524         String triggerName = "";
   525         boolean moveNext = false;
   526 
   527         //Check whether the beginning is in a SQL Plus command
   528         if (sqlPlusLine(ts)) {
   529             return null;
   530         }
   531 
   532         //Get procedure/function name which is the next non whitespace token
   533         moveNext = getNextNonWhitespace(ts, true);
   534         tmp = ts.token();
   535         if (moveNext == false) {
   536             return block;
   537         }
   538 
   539         triggerName = tmp.text().toString();
   540         triggerName = checkForOtherSchema(ts, triggerName);
   541         String alias = "";
   542         if (triggerName.indexOf('&') != -1) {
   543             alias = triggerName;
   544         }
   545         triggerName = getDefine(triggerName);
   546         Token<PlsqlTokenId> customStartToken = null;
   547 
   548         while (moveNext) {
   549             String image = tmp.text().toString();
   550             PlsqlTokenId tokenID = tmp.id();
   551 
   552             if ((tmp != null) && (!image.equals(";")) && (tmp.offset(tokenHierarchy) > endParse)) {
   553                 break;
   554             }
   555 
   556             //We might have come to the end of the procedure/function declaration
   557             if ((tokenID == PlsqlTokenId.OPERATOR) && image.equals(";")) {
   558                 //check whether previous Non white space token to the identifier is END
   559                 int offset = ts.offset();
   560                 getPreviousNonWhitespace(ts, true);
   561                 Token<PlsqlTokenId> previousNWS = ts.token();
   562                 String prevText = previousNWS.text().toString();
   563                 getPreviousNonWhitespace(ts, true);
   564                 previousNWS = ts.token();
   565                 if (alias.equals("")) {
   566                     if ((prevText.equalsIgnoreCase(triggerName)
   567                             && previousNWS.text().toString().equalsIgnoreCase("END"))
   568                             || prevText.equalsIgnoreCase("END")) {
   569                         ts.move(offset);
   570                         moveNext = ts.moveNext();
   571                         moveNext = ts.moveNext();
   572                         block = new PlsqlBlock(triggerBegin.offset(tokenHierarchy),
   573                                 ts.offset(), triggerName, alias, PlsqlBlockType.TRIGGER);
   574                         checkPrefix(triggerBegin.offset(tokenHierarchy), ts, block);
   575                         break;
   576                     }
   577                 } else {
   578                     if ((prevText.equalsIgnoreCase(alias)
   579                             && previousNWS.text().toString().equalsIgnoreCase("END"))
   580                             || prevText.equalsIgnoreCase("END")) {
   581                         ts.move(offset);
   582                         moveNext = ts.moveNext();
   583                         moveNext = ts.moveNext();
   584                         block = new PlsqlBlock(triggerBegin.offset(tokenHierarchy),
   585                                 ts.offset(), triggerName, alias, PlsqlBlockType.TRIGGER);
   586                         checkPrefix(triggerBegin.offset(tokenHierarchy), ts, block);
   587                         break;
   588                     }
   589                 }
   590                 ts.move(offset);
   591                 moveNext = ts.moveNext();
   592             } else if (image.equalsIgnoreCase("TABLE")
   593                     || image.equalsIgnoreCase("INDEX")
   594                     || image.equalsIgnoreCase("SELECT")
   595                     || image.equalsIgnoreCase("UPDATE")
   596                     || image.equalsIgnoreCase("DELETE")
   597                     || image.equalsIgnoreCase("INSERT")
   598                     || image.equalsIgnoreCase("MERGE")
   599                     || image.equalsIgnoreCase("DROP")
   600                     || image.equalsIgnoreCase("SEQUENCE")) {
   601                 if (!isNotBlockStart(tmp, ts)) {
   602                     int offset = tmp.offset(tokenHierarchy);
   603                     PlsqlBlock child = checkStatementBlock(tmp, ts, parentBlocks);
   604                     if (child == null) {//If inner check seems to have failed need to continue this one
   605 
   606                         ts.move(offset);
   607                         ts.moveNext();
   608                     } else {
   609                         if (checkExisting(child, lstChild) == false) {
   610                             lstChild.add(child);
   611                         }
   612                     }
   613                 }
   614             } else if (image.equalsIgnoreCase("PROCEDURE")) {
   615                 int beforeOff = tmp.offset(tokenHierarchy);
   616                 PlsqlBlock child = checkMethod(tmp, ts, PlsqlBlockType.PROCEDURE_IMPL, lstChild);
   617                 if (child == null) {//If inner check seems to have failed need to continue this one
   618                     ts.move(beforeOff);
   619                     ts.moveNext();
   620                 } else {
   621                     if (checkExisting(child, lstChild) == false) {
   622                         lstChild.add(child);
   623                     }
   624                 }
   625             } //Inner procedure
   626             else if (image.equalsIgnoreCase("FUNCTION")) {
   627                 int beforeOff = tmp.offset(tokenHierarchy);
   628                 PlsqlBlock child = checkMethod(tmp, ts, PlsqlBlockType.FUNCTION_IMPL, lstChild);
   629                 if (child == null) {//If inner check seems to have failed need to continue this one
   630 
   631                     ts.move(beforeOff);
   632                     ts.moveNext();
   633                 } else {
   634                     if (checkExisting(child, lstChild) == false) {
   635                         lstChild.add(child);
   636                     }
   637                 }
   638             } else if (image.equalsIgnoreCase("CREATE")
   639                     || image.equalsIgnoreCase("/")
   640                     || image.equalsIgnoreCase("PACKAGE")) {
   641                 break;
   642             } else if (tokenID == PlsqlTokenId.LINE_COMMENT) {
   643                 //only single comment line
   644                 if (image.toUpperCase(Locale.ENGLISH).contains("<FOLD>")) {
   645                     customStartToken = tmp;
   646                 } else if (image.toUpperCase(Locale.ENGLISH).contains("<END-FOLD>")) {
   647                     if (customStartToken != null) {
   648                         String name = customStartToken.text().toString();
   649                         int index = name.toUpperCase(Locale.ENGLISH).indexOf("<FOLD>");
   650                         name = name.substring(index + 7).trim();
   651                         if (ts.moveNext()) {
   652                             tmp = ts.token();
   653                             PlsqlBlock custom = new PlsqlBlock(customStartToken.offset(tokenHierarchy),
   654                                     tmp.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
   655                             customFoldBlocks.add(custom);
   656                         }
   657                         customStartToken = null;
   658                     }
   659                 } else {
   660                     PlsqlBlock child = checkComment(tmp, ts);
   661                     if (child != null) {
   662                         if (checkExisting(child, lstChild) == false) {
   663                             lstChild.add(child);
   664                         }
   665                     }
   666                 }
   667             } else if (tokenID == PlsqlTokenId.BLOCK_COMMENT) {
   668                 int start = tmp.offset(tokenHierarchy);
   669                 PlsqlBlock child = new PlsqlBlock(start,
   670                         start + tmp.length(), "BLOCK COMMENT", "", PlsqlBlockType.COMMENT);
   671                 if (child != null) {
   672                     if (checkExisting(child, lstChild) == false) {
   673                         lstChild.add(child);
   674                     }
   675                 }
   676             }
   677 
   678             moveNext = ts.moveNext();
   679             tmp = ts.token();
   680         }
   681 
   682         if (block != null) {
   683             //add children
   684             addChildren(block, lstChild, parentBlocks);
   685         }
   686 
   687         return block;
   688     }
   689 
   690     /**
   691      * Method that will check the prefix of the given block and change the block values accordingly
   692      *
   693      * @param startOffset
   694      * @param ts
   695      * @param begin
   696      */
   697     private void checkPrefix(int startOffset, TokenSequence<PlsqlTokenId> ts, PlsqlBlock block) {
   698         String prefix = "";
   699         int offset = ts.offset();
   700         ts.move(startOffset);
   701         ts.moveNext();
   702         Token<PlsqlTokenId> token = ts.token();
   703         int beginOffset = startOffset;
   704 
   705         while (ts.movePrevious()) {
   706             token = ts.token();
   707             String image = token.text().toString();
   708 
   709             if (image.contains("\n") || (token.id() != PlsqlTokenId.KEYWORD && token.id() != PlsqlTokenId.WHITESPACE)) {
   710                 break;
   711             }
   712 
   713             prefix = token.text().toString() + prefix;
   714             if (token.id() != PlsqlTokenId.WHITESPACE) {
   715                 beginOffset = ts.offset();
   716             }
   717         }
   718 
   719         ts.move(offset);
   720         ts.moveNext();
   721         block.setStartOffset(beginOffset);
   722         block.setPrefix(prefix);
   723     }
   724 
   725     /**
   726      * Check whether there is a block existing with the given offset as the start offset
   727      *
   728      * @param blockHierarchy
   729      * @param offset
   730      * @return
   731      */
   732     private boolean isBlockStartExisting(List<PlsqlBlock> blockHierarchy, int offset) {
   733         boolean isExisting = false;
   734         for (int i = blockHierarchy.size() - 1; i >= 0; i--) {
   735             PlsqlBlock tmp = blockHierarchy.get(i);
   736             if (tmp.getStartOffset() == offset) {
   737                 isExisting = true;
   738                 break;
   739             }
   740 
   741             if (!isExisting) {
   742                 if (isBlockStartExisting(tmp.getChildBlocks(), offset)) {
   743                     isExisting = true;
   744                     break;
   745                 }
   746             }
   747         }
   748 
   749         return isExisting;
   750     }
   751 
   752     private boolean isEqual(PlsqlBlock parent, PlsqlBlock block) {
   753         if ((parent == null) || (block == null)) {
   754             return false;
   755         }
   756         if ((parent.getStartOffset() == block.getStartOffset())
   757                 || (parent.getEndOffset() == block.getEndOffset())
   758                 || (parent.getName().equalsIgnoreCase(block.getName()))) {
   759             return true;
   760         }
   761         return false;
   762     }
   763 
   764     private String readLine(TokenSequence<PlsqlTokenId> ts, Token<PlsqlTokenId> token) {
   765         String line = token.toString();
   766         while (ts.moveNext()) {
   767             token = ts.token();
   768             if (token.id() == PlsqlTokenId.WHITESPACE && token.text().toString().contains("\n")) {
   769                 ts.movePrevious();
   770                 break;
   771             }
   772 
   773             line = line + token.toString();
   774         }
   775 
   776         return line;
   777     }
   778 
   779     /**
   780      * Method that will remove the begin block of this declare block if there
   781      *
   782      * @param declareBlock
   783      */
   784     private void removeChildBegin(PlsqlBlock declareBlock) {
   785         //can check from the root hierarchies since begin/declare cannot be child blocks
   786         for (int i = blockHierarchy.size() - 1; i >= 0; i--) {
   787             PlsqlBlock tmp = blockHierarchy.get(i);
   788             if ((tmp.getStartOffset() > declareBlock.getStartOffset())
   789                     && (tmp.getEndOffset() == declareBlock.getEndOffset())
   790                     && (tmp.getType() == PlsqlBlockType.BEGIN_END)) {
   791                 blockHierarchy.remove(tmp);
   792             }
   793         }
   794     }
   795 
   796     /**
   797      * Change offsets of the blocks below the area
   798      *
   799      * @param blockHier
   800      * @param endParse
   801      * @param length
   802      */
   803     private void changeOffSet(List<PlsqlBlock> blockHier, int offset, int length, boolean add, boolean adjust) {
   804         int count = blockHier.size();
   805         int start = 0;
   806         int end = 0;
   807         for (int i = 0; i < count; i++) {
   808             PlsqlBlock temp = blockHier.get(i);
   809             start = temp.getStartOffset();
   810             end = temp.getEndOffset();
   811             if (temp.getStartOffset() >= offset) {
   812                 if (temp.getPreviousStart() == -1) {
   813                     temp.setPreviousStart(start);
   814                 }
   815                 if (start + length < 0) {
   816                     temp.setStartOffset(0);
   817                 } else {
   818                     temp.setStartOffset(start + length);
   819                     if (startParse == start && adjust) //changing offsets of toBeRemoved
   820                     {
   821                         startParse = start + length;
   822                     }
   823                 }
   824 
   825                 if (temp.getPreviousEnd() == -1) {
   826                     temp.setPreviousEnd(end);
   827                 }
   828                 if (end + length < 0) {
   829                     temp.setEndOffset(0);
   830                 } else {
   831                     temp.setEndOffset(end + length);
   832                     if (endParse == end && adjust) //changing offsets of toBeRemoved
   833                     {
   834                         endParse = end + length;
   835                     }
   836                 }
   837 
   838                 if (add) {
   839                     addToChangedBlocks(temp);
   840                 }
   841                 changeOffSet(temp.getChildBlocks(), offset, length, add, adjust);
   842             } else if (temp.getEndOffset() >= offset) {
   843                 if (temp.getPreviousEnd() == -1) {
   844                     temp.setPreviousEnd(end);
   845                 }
   846                 if (end + length < 0) {
   847                     temp.setEndOffset(0);
   848                 } else {
   849                     temp.setEndOffset(end + length);
   850                     if (endParse == end && adjust) //changing offsets of toBeRemoved
   851                     {
   852                         endParse = end + length;
   853                     }
   854                 }
   855 
   856                 if (add) {
   857                     addToChangedBlocks(temp);
   858                 }
   859                 changeOffSet(temp.getChildBlocks(), offset, length, add, adjust);
   860             }
   861         }
   862     }
   863 
   864     /**
   865      * Method that will check whether there are DEFINE statements in the affected area
   866      *
   867      * @param doc
   868      * @param startOffset
   869      * @param endOffset
   870      */
   871     private void checkAffected(Document doc, int startOffset, int endOffset) throws BadLocationException {
   872         int length = endOffset - startOffset;
   873         String changedText = doc.getText(startOffset, length);
   874 
   875         if ((changedText.toUpperCase(Locale.ENGLISH).indexOf("DEFINE ") != -1)
   876                 || (changedText.toUpperCase(Locale.ENGLISH).indexOf("DEFIN ") != -1)
   877                 || (changedText.toUpperCase(Locale.ENGLISH).indexOf("DEFI ") != -1)
   878                 || (changedText.toUpperCase(Locale.ENGLISH).indexOf("DEF ") != -1)) {
   879             isDefineChanged = true;
   880         }
   881     }
   882 
   883     /**
   884      * Method that will make CURSOR blocks
   885      *
   886      * @param tempToken
   887      * @param ts
   888      * @param parentBlocks
   889      * @return
   890      */
   891     private PlsqlBlock checkCursor(Token<PlsqlTokenId> current, TokenSequence<PlsqlTokenId> ts, List<PlsqlBlock> parentBlocks) {
   892         Token<PlsqlTokenId> cursorBegin = current;
   893         Token<PlsqlTokenId> tmp = current;
   894         Token<PlsqlTokenId> cursorEnd = null;
   895         PlsqlBlock block = null;
   896         List<PlsqlBlock> lstChild = new ArrayList<PlsqlBlock>();
   897         boolean moveNext = false;
   898 
   899         //Check whether the beginning is in a SQL Plus command
   900         if (sqlPlusLine(ts)) {
   901             return null;
   902         }
   903 
   904         //Get next token which is the name
   905         int offset = ts.offset();
   906         moveNext = getNextNonWhitespace(ts, false);
   907         tmp = ts.token();
   908         if (moveNext == false) {
   909             ts.move(offset);
   910             ts.moveNext();
   911             return block;
   912         }
   913         String cursorName = tmp.text().toString();
   914         String alias = "";
   915         if (cursorName.indexOf('&') != -1) {
   916             alias = cursorName;
   917         }
   918         cursorName = getDefine(cursorName);
   919 
   920         //Next token has to be IS or ( if not leave
   921         moveNext = getNextNonWhitespace(ts, false);
   922         tmp = ts.token();
   923         boolean isFound = false;
   924         boolean parameter = false;
   925         Token<PlsqlTokenId> customStartToken = null;
   926 
   927         while (moveNext) {
   928             PlsqlTokenId tokenID = tmp.id();
   929             String image = tmp.text().toString();
   930 
   931             if ((tmp != null) && (!image.equals(";")) && (tmp.offset(tokenHierarchy) > endParse)) {
   932                 break;
   933             }
   934 
   935             //When we have come up to ';' stop
   936             if ((tokenID == PlsqlTokenId.OPERATOR) && (image.equals(";"))) {
   937                 if (isFound) {
   938                     if (ts.moveNext()) {
   939                         tmp = ts.token();
   940                     }
   941                     cursorEnd = tmp;
   942                     break;
   943                 } else {
   944                     ts.move(offset);
   945                     ts.moveNext();
   946                     return null;
   947                 }
   948             } else if ((tokenID == PlsqlTokenId.LPAREN) && (!isFound)) {
   949                 parameter = true;
   950             } else if ((tokenID == PlsqlTokenId.RPAREN) && (!isFound)) {
   951                 parameter = false;
   952             } else if (tokenID == PlsqlTokenId.KEYWORD) {
   953                 if (image.equalsIgnoreCase("IS")) {
   954                     isFound = true;
   955                 } else if ((!parameter) && //If this keyword is a parameter inside cursor ignore
   956                         ((image.equalsIgnoreCase("SUBTYPE"))
   957                         || (image.equalsIgnoreCase("CONSTANT"))
   958                         || (image.equalsIgnoreCase("NUMBER"))
   959                         || (image.equalsIgnoreCase("VARCHAR2")))) { //Avoid catching ';' of other statements
   960 
   961                     return null;
   962                 } else if ((image.equalsIgnoreCase("VIEW"))
   963                         || (image.equalsIgnoreCase("PROCEDURE"))
   964                         || (image.equalsIgnoreCase("FUNCTION"))
   965                         || (image.equalsIgnoreCase("BEGIN"))
   966                         || (image.equalsIgnoreCase("DECLARE"))
   967                         || (image.equalsIgnoreCase("CREATE"))
   968                         || (image.equalsIgnoreCase("CURSOR"))
   969                         || (image.equalsIgnoreCase("EXCEPTION"))
   970                         || (image.equalsIgnoreCase("PRAGMA"))
   971                         || (image.equalsIgnoreCase("END"))
   972                         || (image.equalsIgnoreCase("COMMENT"))) { //Avoid catching ';' of other statements
   973 
   974                     return null;
   975                 }
   976             } else if (tokenID == PlsqlTokenId.LINE_COMMENT) {
   977                 //only single comment line
   978                 if (image.toUpperCase(Locale.ENGLISH).contains("<FOLD>")) {
   979                     customStartToken = tmp;
   980                 } else if (image.toUpperCase(Locale.ENGLISH).contains("<END-FOLD>")) {
   981                     if (customStartToken != null) {
   982                         String name = customStartToken.text().toString();
   983                         int index = name.toUpperCase(Locale.ENGLISH).indexOf("<FOLD>");
   984                         name = name.substring(index + 7).trim();
   985                         if (ts.moveNext()) {
   986                             tmp = ts.token();
   987                             PlsqlBlock custom = new PlsqlBlock(customStartToken.offset(tokenHierarchy),
   988                                     tmp.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
   989                             customFoldBlocks.add(custom);
   990                         }
   991                         customStartToken = null;
   992                     }
   993                 } else {
   994                     PlsqlBlock child = checkComment(tmp, ts);
   995                     if (child != null) {
   996                         if (checkExisting(child, lstChild) == false) {
   997                             lstChild.add(child);
   998                         }
   999                     }
  1000                 }
  1001             } else if (tokenID == PlsqlTokenId.BLOCK_COMMENT) {
  1002                 int start = tmp.offset(tokenHierarchy);
  1003                 PlsqlBlock child = new PlsqlBlock(start,
  1004                         start + tmp.length(), "BLOCK COMMENT", "", PlsqlBlockType.COMMENT);
  1005                 if (child != null) {
  1006                     if (checkExisting(child, lstChild) == false) {
  1007                         lstChild.add(child);
  1008                     }
  1009                 }
  1010             }
  1011 
  1012             moveNext = ts.moveNext();
  1013             tmp = ts.token();
  1014         }
  1015 
  1016         if (cursorEnd != null) {
  1017             block = new PlsqlBlock(cursorBegin.offset(tokenHierarchy), ts.offset(),
  1018                     cursorName, alias, PlsqlBlockType.CURSOR);
  1019         }
  1020 
  1021         if (block != null) {
  1022             //add children
  1023             addChildren(block, lstChild, parentBlocks);
  1024         }
  1025 
  1026         return block;
  1027     }
  1028 
  1029     /**
  1030      * Check whether we have caught a begin of a declare block
  1031      *
  1032      * @param ts
  1033      * @param immediate
  1034      * @return
  1035      */
  1036     private boolean isDeclare(TokenSequence<PlsqlTokenId> ts, List<PlsqlBlock> immediate) {
  1037         int offset = ts.offset();
  1038         Token<PlsqlTokenId> token = ts.token();
  1039         Token<PlsqlTokenId> tokenPre = ts.token();
  1040 
  1041         while (ts.movePrevious()) {
  1042             token = ts.token();
  1043             String image = token.text().toString();
  1044             if (image.equalsIgnoreCase("DECLARE") && (!isBlockStartExisting(immediate, ts.offset()))) {
  1045                 PlsqlBlock block = checkDeclareBlock(token, ts, immediate);
  1046                 if (block != null && (checkExisting(block, immediate) == false)) {//If inner check seems to have failed need to continue this one
  1047                     immediate.add(block);
  1048                     newBlocks.add(block);
  1049                 } else {
  1050                     //Since we have not found the block we have to avoid getting caught up in a loop
  1051                     ts.move(offset);
  1052                     ts.moveNext();
  1053                 }
  1054                 return true;
  1055             } else if ((image.equals("/")) || (image.equalsIgnoreCase("BEGIN"))
  1056                     || ((image.equalsIgnoreCase("END")) && (tokenPre.text().toString().equals(";")))) {
  1057                 break;
  1058             }
  1059 
  1060             if ((token.id() != PlsqlTokenId.WHITESPACE)
  1061                     && (token.id() != PlsqlTokenId.LINE_COMMENT)
  1062                     && (token.id() != PlsqlTokenId.BLOCK_COMMENT)) {
  1063                 tokenPre = token;
  1064             }
  1065         }
  1066 
  1067         ts.move(offset);
  1068         ts.moveNext();
  1069         return false;
  1070     }
  1071 
  1072     /**
  1073      * Check whether the given block is already there in block hierarchy
  1074      *
  1075      * @param block
  1076      * @param childList
  1077      * @return
  1078      */
  1079     private boolean checkExisting(PlsqlBlock block, List<PlsqlBlock> childList) {
  1080         boolean existing = false;
  1081         int count = childList.size();
  1082         for (int i = 0; i < count; i++) {
  1083             PlsqlBlock tmp = childList.get(i);
  1084             if ((tmp.getName().equals(block.getName()) && tmp.getEndOffset() == block.getEndOffset())
  1085                     || (tmp.getName().equals(block.getName()) && tmp.getStartOffset() == block.getStartOffset())
  1086                     || (tmp.getEndOffset() == block.getEndOffset() && tmp.getStartOffset() == block.getStartOffset())) {
  1087                 existing = true;
  1088                 break;
  1089             }
  1090         }
  1091 
  1092         return existing;
  1093     }
  1094 
  1095     /**
  1096      * Clear internal variables used in a parse
  1097      */
  1098     private void clear() {
  1099         isDefineChanged = false;
  1100         startParse = 0;
  1101         endParse = 0;
  1102         changedLength = 0;
  1103         newBlocks = new ArrayList<PlsqlBlock>();
  1104         toBeRemoved = new ArrayList<PlsqlBlock>();
  1105         changedBlocks = new ArrayList<PlsqlBlock>();
  1106         unsuccessBlocks = new HashSet<Integer>();
  1107         resetPreviousValues(blockHierarchy);
  1108     }
  1109 
  1110     private void resetPreviousValues(List<PlsqlBlock> blockList) {
  1111         for (PlsqlBlock block : blockList) {
  1112             block.setPreviousStart(-1);
  1113             block.setPreviousEnd(-1);
  1114             resetPreviousValues(block.getChildBlocks());
  1115         }
  1116     }
  1117 
  1118     public void beforeCaseChange() {
  1119         caseChangeInProgress = true;
  1120     }
  1121 
  1122     public void afterCaseChange() {
  1123         caseChangeInProgress = false;
  1124     }
  1125 
  1126     public void beforeSave(Document document) {
  1127         saveInProgress = true;
  1128         synchronized (updateLock) {
  1129             updateEvents.clear();
  1130         }
  1131     }
  1132 
  1133     public boolean isSaveInProgress() {
  1134         return saveInProgress || caseChangeInProgress;
  1135     }
  1136 
  1137     public synchronized void afterSave(Document document) {
  1138         setChanged();
  1139         //initHierarchy(document);
  1140         reParse(document);
  1141         parseAliases();
  1142         notifyObservers(document);
  1143         clearChanged();
  1144         saveInProgress = false;
  1145     }
  1146 
  1147     private EventProperties addNewEvent(DocumentEvent e, DocumentEvent.EventType mode) {
  1148         EventProperties eventProperties = new EventProperties(this);
  1149         eventProperties.document = e.getDocument();
  1150         eventProperties.offset = e.getOffset();
  1151         eventProperties.length = e.getLength();
  1152         eventProperties.mode = mode;
  1153         updateEvents.addLast(eventProperties);
  1154         return eventProperties;
  1155 
  1156     }
  1157 
  1158     private void removeBlock(PlsqlBlock block, List<PlsqlBlock> lstBlocks) {
  1159         boolean removed = false;
  1160         if (!lstBlocks.remove(block)) {
  1161             if (removeBlock(blockHierarchy, block)) {
  1162                 toBeRemoved.add(block);
  1163                 removed = true;
  1164             }
  1165         } else {
  1166             toBeRemoved.add(block);
  1167             removed = true;
  1168         }
  1169 
  1170         if (removed) {
  1171             //If this block is there in changed blocks remove it
  1172             removeBlock(changedBlocks, block);
  1173             if (startParse > block.getStartOffset()) {
  1174                 startParse = block.getStartOffset();
  1175             }
  1176             if (endParse < block.getEndOffset()) {
  1177                 endParse = block.getEndOffset();
  1178             }
  1179         }
  1180     }
  1181 
  1182     private void addToChangedBlocks(PlsqlBlock temp) {
  1183         int count = changedBlocks.size();
  1184         boolean found = false;
  1185         for (int i = 0; i < count; i++) {
  1186             PlsqlBlock tmp = changedBlocks.get(i);
  1187             if (tmp.getStartOffset() == temp.getStartOffset() && tmp.getEndOffset() == temp.getEndOffset()) {
  1188                 found = true;
  1189                 break;
  1190             }
  1191         }
  1192 
  1193         if (!found) {
  1194             changedBlocks.add(temp);
  1195         }
  1196     }
  1197 
  1198     private String checkForOtherSchema(TokenSequence<PlsqlTokenId> ts, String currentName) {
  1199         int offset = ts.offset();
  1200         if (ts.moveNext()) {
  1201             Token<PlsqlTokenId> token = ts.token();
  1202             if (token.id() == PlsqlTokenId.DOT) {
  1203                 if (ts.moveNext()) {
  1204                     token = ts.token();
  1205                     if (token.id() == PlsqlTokenId.DOT) {
  1206                         if (ts.moveNext()) {
  1207                             return ts.token().toString();
  1208                         }
  1209                     } else {
  1210                         return ts.token().toString();
  1211                     }
  1212                 }
  1213             }
  1214         }
  1215 
  1216         //Reset the original location
  1217         ts.move(offset);
  1218         ts.moveNext();
  1219 
  1220         return currentName;
  1221     }
  1222 
  1223     private static class EventProperties {
  1224 
  1225         public int offset = -1;
  1226         public int length = -1;
  1227         public Document document = null;
  1228         DocumentEvent.EventType mode = null;
  1229         public PlsqlBlockFactory blockFactory = null;
  1230 
  1231         public EventProperties(PlsqlBlockFactory blockFactory) {
  1232             this.blockFactory = blockFactory;
  1233         }
  1234     }
  1235 
  1236     private static class UpdateBlocksThread implements Runnable {
  1237 
  1238         @Override
  1239         public void run() {
  1240             synchronized (updateLock) {
  1241                 while (updateEvents.size() > 0) {
  1242                     EventProperties event = updateEvents.getFirst();
  1243                     Document doc = event.document;
  1244                     List<EventProperties> docList = new ArrayList<EventProperties>();
  1245 
  1246                     while (event != null && event.document.equals(doc)) {
  1247                         updateEvents.removeFirst();
  1248                         docList.add(event);
  1249                         event = null;
  1250                         if (updateEvents.size() > 0) {
  1251                             event = updateEvents.getFirst();
  1252                         }
  1253                     }
  1254 
  1255                     docList.get(0).blockFactory.doUpdate(doc, docList);
  1256                 }
  1257             }
  1258         }
  1259     }
  1260 
  1261     private synchronized void doUpdate(final Document document, final List<EventProperties> docList) {
  1262         LOG.log(Level.FINE, "doUpdate", new Object[]{document, docList});
  1263 
  1264         //make sure that the updates are run in the Swing thread
  1265         SwingUtilities.invokeLater(new Runnable() {
  1266             @Override
  1267             public void run() {
  1268                 setChanged();
  1269                 updateBlocks(document, docList);
  1270                 parseAliases();
  1271                 notifyObservers(document);
  1272                 clearChanged();
  1273             }
  1274         });
  1275     }
  1276 
  1277     private void addUpdateEvent(DocumentEvent e, DocumentEvent.EventType mode) {
  1278         synchronized (updateLock) {
  1279             if (updateBlocksTask == null) {
  1280                 updateBlocksTask = RP.create(new UpdateBlocksThread());
  1281             }
  1282             updateBlocksTask.schedule(DEFAULT_WAIT_TIME);
  1283             addNewEvent(e, mode);
  1284         }
  1285     }
  1286 
  1287     /**
  1288      * Event fired on insert
  1289      *
  1290      * @param e
  1291      */
  1292     @Override
  1293     public void insertUpdate(DocumentEvent e) {
  1294         LOG.log(Level.FINER, "insertUpdate", e);
  1295         if (isSaveInProgress()) {
  1296             return;
  1297         }
  1298         addUpdateEvent(e, DocumentEvent.EventType.INSERT);
  1299     }
  1300 
  1301     /**
  1302      * Event fired in remove
  1303      *
  1304      * @param e
  1305      */
  1306     @Override
  1307     public void removeUpdate(DocumentEvent e) {
  1308         LOG.log(Level.FINER, "removeUpdate", e);
  1309         if (isSaveInProgress()) {
  1310             return;
  1311         }
  1312         addUpdateEvent(e, DocumentEvent.EventType.REMOVE);
  1313     }
  1314 
  1315     /**
  1316      * triggered when opening a different document
  1317      *
  1318      * @param e
  1319      */
  1320     @Override
  1321     public void changedUpdate(DocumentEvent e) {
  1322         //It seems we don't have to handle this case since initHierarchy
  1323         // will always be called before this
  1324     }
  1325 
  1326     /**
  1327      * Update block hierarchy on a document event
  1328      *
  1329      * @param e
  1330      * @param action
  1331      */
  1332     public synchronized void initHierarchy(Document doc) {
  1333         clear();
  1334 
  1335         if (doc == null) {
  1336             return;
  1337         }
  1338 
  1339         docStartOffset = doc.getStartPosition().getOffset();
  1340         docEndOffset = doc.getEndPosition().getOffset();
  1341         Object obj = doc.getProperty("Listener");
  1342 
  1343         if ((obj == null) || (!obj.equals("YES"))) {
  1344             startParse = docStartOffset;
  1345             endParse = docEndOffset - 1;
  1346 
  1347             //clean block hierarchy
  1348             blockHierarchy.clear();
  1349             customFoldBlocks.clear();
  1350             getAliases(doc);
  1351             generateBlocks(doc);
  1352             //This property is added only to ensure that document listener is added only once
  1353             doc.putProperty("Listener", "YES");
  1354             doc.addDocumentListener(this);
  1355         }
  1356     }
  1357 
  1358     /**
  1359      * Method that will return the block within the start & end parse
  1360      *
  1361      * @param start
  1362      * @param end
  1363      * @return
  1364      */
  1365     private PlsqlBlock getParentBlock(List<PlsqlBlock> lstBlock, int start, int end) {
  1366         PlsqlBlock parent = null;
  1367         int count = lstBlock.size();
  1368         for (int i = 0; i < count; i++) {
  1369             PlsqlBlock tmp = lstBlock.get(i);
  1370             if ((tmp.getStartOffset() < start) && (tmp.getEndOffset() > end)) {
  1371                 PlsqlBlock child = getParentBlock(tmp.getChildBlocks(), start, end);
  1372                 if (child != null) {
  1373                     parent = child;
  1374                 } else {
  1375                     parent = tmp;
  1376                 }
  1377 
  1378                 break;
  1379             }
  1380         }
  1381 
  1382         return parent;
  1383     }
  1384 
  1385     /**
  1386      * Get the line offset of the beginning of this block end line
  1387      *
  1388      * @param doc
  1389      * @param parent
  1390      * @return
  1391      */
  1392     private int getPreLineOfBlockEnd(Document doc, PlsqlBlock parent) {
  1393         TokenHierarchy tokenHier = TokenHierarchy.get(doc);
  1394         @SuppressWarnings("unchecked")
  1395         TokenSequence<PlsqlTokenId> ts = tokenHier.tokenSequence(PlsqlTokenId.language());
  1396         if (ts == null) {
  1397             return parent.getEndOffset();
  1398         }
  1399         int preEnd = parent.getEndOffset();
  1400 
  1401         //go to the previous line break
  1402         ts.move(preEnd);
  1403         boolean movePrevious = ts.movePrevious();
  1404         Token<PlsqlTokenId> tokenPre = ts.token();
  1405 
  1406         while (movePrevious) {
  1407             if (tokenPre.text().toString().contains("\n")) {
  1408                 preEnd = tokenPre.offset(tokenHier);
  1409                 break;
  1410             }
  1411             movePrevious = ts.movePrevious();
  1412             tokenPre = ts.token();
  1413         }
  1414 
  1415         return preEnd;
  1416     }
  1417 
  1418     private boolean isNotBlockStart(Token<PlsqlTokenId> current, TokenSequence<PlsqlTokenId> ts) {
  1419         boolean isNotBlockStart = false;
  1420         int offset = current.offset(tokenHierarchy);
  1421         Token<PlsqlTokenId> token = current;
  1422         Token<PlsqlTokenId> tokenPre = current;
  1423         int loopCount = 0;
  1424         while (ts.movePrevious()) {
  1425             token = ts.token();
  1426 
  1427             if (loopCount == 0 && token.id() == PlsqlTokenId.DOT) {
  1428                 isNotBlockStart = true;
  1429                 break;
  1430             } else if (token.id() == PlsqlTokenId.WHITESPACE) {
  1431                 String preText = tokenPre.text().toString();
  1432                 if (token.text().toString().contains("\n")) {
  1433                     if (preText.equalsIgnoreCase("TYPE")
  1434                             || preText.equalsIgnoreCase("GRANT")) {
  1435                         isNotBlockStart = true;
  1436                     }
  1437                     break;
  1438                 }
  1439             } else if (token.text().toString().equalsIgnoreCase("TYPE")) {
  1440                 isNotBlockStart = true;
  1441                 break;
  1442             } else {
  1443                 tokenPre = token;
  1444             }
  1445             loopCount++;
  1446         }
  1447 
  1448         if (token.text().toString().equalsIgnoreCase("TYPE")
  1449                 || token.text().toString().equalsIgnoreCase("GRANT")) {
  1450             isNotBlockStart = true;
  1451         }
  1452 
  1453         ts.move(offset);
  1454         ts.moveNext();
  1455         return isNotBlockStart;
  1456     }
  1457 
  1458     /**
  1459      * Get the line offset of the second line of this block start
  1460      *
  1461      * @param doc
  1462      * @param parent
  1463      * @return
  1464      */
  1465     private int getSecondLineOfBlock(Document doc, PlsqlBlock parent) {
  1466         TokenHierarchy tokenHier = TokenHierarchy.get(doc);
  1467         @SuppressWarnings("unchecked")
  1468         TokenSequence<PlsqlTokenId> ts = tokenHier.tokenSequence(PlsqlTokenId.language());
  1469         if (ts == null) {
  1470             return parent.getStartOffset();
  1471         }
  1472         int preStart = parent.getStartOffset();
  1473         ts.move(preStart);
  1474         boolean moveNext = ts.moveNext();
  1475         Token<PlsqlTokenId> tokenNext = ts.token();
  1476 
  1477         while (moveNext) {
  1478             if (tokenNext.text().toString().contains("\n")) {
  1479                 preStart = tokenNext.offset(tokenHier);
  1480                 break;
  1481             }
  1482             moveNext = ts.moveNext();
  1483             tokenNext = ts.token();
  1484         }
  1485 
  1486         return preStart;
  1487     }
  1488 
  1489     /**
  1490      * If any defines are changed change the affected names
  1491      */
  1492     private void parseAliases() {
  1493         if (!isDefineChanged) {
  1494             return;
  1495         }
  1496 
  1497         int size = blockHierarchy.size();
  1498         for (int i = 0; i < size; i++) {
  1499             PlsqlBlock block = blockHierarchy.get(i);
  1500             evaluateBlock(block);
  1501         }
  1502     }
  1503 
  1504     public boolean isAliasesChanged() {
  1505         return isDefineChanged;
  1506     }
  1507 
  1508     /**
  1509      * Method that will evaluate the given block and decide whether the name has to be changed
  1510      *
  1511      * @param block
  1512      */
  1513     private void evaluateBlock(PlsqlBlock block) {
  1514         String alias = block.getAlias();
  1515         if (!alias.equals("")) {
  1516             block.setName(getDefine(alias));
  1517         }
  1518 
  1519         int childCount = block.getChildBlocks().size();
  1520         for (int i = 0; i < childCount; i++) {
  1521             PlsqlBlock child = block.getChildBlocks().get(i);
  1522             evaluateBlock(child);
  1523         }
  1524     }
  1525 
  1526     /**
  1527      * Remove given block from the hierarchy
  1528      *
  1529      * @param blockHier
  1530      * @param block
  1531      */
  1532     private boolean removeBlock(List<PlsqlBlock> blockHier, PlsqlBlock block) {
  1533         int count = blockHier.size();
  1534         boolean isFound = false;
  1535 
  1536         for (int i = 0; i < count; i++) {
  1537             PlsqlBlock temp = blockHier.get(i);
  1538 
  1539             if ((temp.getStartOffset() == block.getStartOffset())
  1540                     && (temp.getEndOffset() == block.getEndOffset())) {
  1541                 blockHier.remove(temp);
  1542                 isFound = true;
  1543                 break;
  1544             } else {
  1545                 if ((temp.getStartOffset() < block.getStartOffset())
  1546                         && (temp.getEndOffset() > block.getEndOffset())) { //block is a child
  1547 
  1548                     if (removeBlock(temp.getChildBlocks(), block)) {
  1549                         isFound = true;
  1550                         break;
  1551                     }
  1552                 }
  1553             }
  1554         }
  1555 
  1556         return isFound;
  1557     }
  1558 
  1559     /**
  1560      * Add child blocks enclosed by the change area to the remove list (do not update the parse area here, done only in
  1561      * REMOVE)
  1562      *
  1563      * @param doc
  1564      * @param plsqlBlocks
  1565      * @param toBeRemoved
  1566      * @param startOffset
  1567      * @param endOffset
  1568      * @return
  1569      */
  1570     private void removeEnclosedBlocks(Document doc, List<PlsqlBlock> plsqlBlocks, int startOffset, int endOffset) {
  1571         int count = plsqlBlocks.size();
  1572 
  1573         for (int i = count - 1; i >= 0; i--) {
  1574             PlsqlBlock block = plsqlBlocks.get(i);
  1575 
  1576             if ((block.getEndOffset() <= endOffset)
  1577                     && (block.getStartOffset() >= startOffset)) { //blocks which are enclosed by the affected area
  1578                 removeBlock(block, plsqlBlocks);
  1579             } else {
  1580                 removeEnclosedBlocks(doc, block.getChildBlocks(), startOffset, endOffset);
  1581             }
  1582         }
  1583     }
  1584 
  1585     /**
  1586      * Add child blocks affected by the change area to the remove list and update the parse area
  1587      *
  1588      * @param doc
  1589      * @param plsqlBlocks
  1590      * @param toBeRemoved
  1591      * @param startOffset
  1592      * @param endOffset
  1593      * @return
  1594      */
  1595     private boolean removeBlocksWithin(Document doc, List<PlsqlBlock> plsqlBlocks, int startOffset, int endOffset) {
  1596         boolean blockIsFound = false;
  1597 
  1598         int count = plsqlBlocks.size();
  1599 
  1600         for (int i = count - 1; i >= 0; i--) {
  1601             PlsqlBlock block = plsqlBlocks.get(i);
  1602             int blockStart = block.getStartOffset();
  1603             int blockEnd = block.getEndOffset();
  1604             //assumption on max length of a package/procedure/function first line
  1605             if ((Math.abs(blockEnd - endOffset) < 150) && (checkSameLine(doc, blockEnd, endOffset))) {
  1606                 removeBlock(block, plsqlBlocks);
  1607                 blockIsFound = true;
  1608                 //assumption on max length of a package/procedure/function first line
  1609             } else if ((Math.abs(blockStart - startOffset) < 150) && (checkSameLine(doc, blockStart, startOffset))) {
  1610                 removeBlock(block, plsqlBlocks);
  1611                 blockIsFound = true;
  1612             } else if ((blockEnd < endOffset)
  1613                     && (blockStart > startOffset)) { //blocks which are enclosed by the affected area
  1614                 removeBlock(block, plsqlBlocks);
  1615                 blockIsFound = true;
  1616             } else if ((blockEnd > endOffset) && (blockStart < startOffset)
  1617                     && (block.getType() != PlsqlBlockType.PACKAGE && block.getType() != PlsqlBlockType.PACKAGE_BODY)) {
  1618                 //affected area included in the block we need to remove the block.
  1619                 //Idealy we should remove all types, but concerning on the performance did this
  1620                 //comment might be removed here
  1621                 removeBlock(block, plsqlBlocks);
  1622                 blockIsFound = true;
  1623             } else {
  1624                 boolean isFound = removeBlocksWithin(doc, block.getChildBlocks(), startOffset, endOffset);
  1625                 if (!isFound) {
  1626                     if ((blockStart >= startOffset)
  1627                             && (blockStart <= endOffset)
  1628                             && (blockEnd >= endOffset)) {//first part of the block enclosed
  1629                         removeBlock(block, plsqlBlocks);
  1630                         blockIsFound = true;
  1631                     } else if ((blockEnd <= endOffset)
  1632                             && (blockEnd >= startOffset)
  1633                             && (blockStart <= startOffset)) {//end part of the block enclosed
  1634                         removeBlock(block, plsqlBlocks);
  1635                         blockIsFound = true;
  1636                     } else if ((blockEnd < endOffset)
  1637                             && (blockStart > startOffset)) { //blocks which are enclosed by the affected area
  1638                         removeBlock(block, plsqlBlocks);
  1639                         blockIsFound = true;
  1640                     } else if ((blockEnd > endOffset) && (blockStart < startOffset)
  1641                             && (block.getType() != PlsqlBlockType.PACKAGE && block.getType() != PlsqlBlockType.PACKAGE_BODY)) {
  1642                         //affected area included in the block we need to remove the block.
  1643                         //Idealy we should remove all types, but concerning on the performance did this
  1644                         //comment might be removed here
  1645                         removeBlock(block, plsqlBlocks);
  1646                         blockIsFound = true;
  1647                     }
  1648                 }
  1649             }
  1650         }
  1651         return blockIsFound;
  1652     }
  1653 
  1654     /**
  1655      * Remove custom fold blocks that are there within the parse area
  1656      *
  1657      * @param customFoldBlocks
  1658      * @param startParse
  1659      * @param endParse
  1660      */
  1661     private void removeCustomBlocks(List<PlsqlBlock> customFoldBlocks, int startParse, int endParse) {
  1662         for (int i = customFoldBlocks.size() - 1; i >= 0; i--) {
  1663             PlsqlBlock block = customFoldBlocks.get(i);
  1664             if ((block.getStartOffset() >= startParse)
  1665                     && (block.getEndOffset() <= endParse)) {
  1666                 customFoldBlocks.remove(i);
  1667             } else if ((block.getEndOffset() <= endParse)
  1668                     && (block.getEndOffset() >= startParse)
  1669                     && (block.getStartOffset() <= startParse)) {
  1670                 customFoldBlocks.remove(i);
  1671             } else if ((block.getStartOffset() >= startParse)
  1672                     && (block.getStartOffset() <= endParse)
  1673                     && (block.getEndOffset() >= endParse)) {
  1674                 customFoldBlocks.remove(i);
  1675             }
  1676         }
  1677     }
  1678 
  1679     /**
  1680      * If given block exists in the hier delete
  1681      *
  1682      * @param child
  1683      * @param parentBlocks
  1684      */
  1685     private void removeFromParent(PlsqlBlock child, List<PlsqlBlock> parentBlocks) {
  1686         for (int i = 0; i < parentBlocks.size(); i++) {
  1687             PlsqlBlock tmp = parentBlocks.get(i);
  1688             if ((tmp.getEndOffset() == child.getEndOffset())
  1689                     && (tmp.getStartOffset() == child.getStartOffset())) {
  1690                 parentBlocks.remove(tmp);
  1691                 break;
  1692             }
  1693         }
  1694     }
  1695 
  1696     private boolean sqlPlusLine(TokenSequence<PlsqlTokenId> ts) {
  1697         int offset = ts.offset();
  1698         boolean isSqlPlus = false;
  1699         Token<PlsqlTokenId> token = null;
  1700         Token<PlsqlTokenId> tokenPre = null;
  1701         while (ts.movePrevious()) {
  1702             token = ts.token();
  1703             if (token.id() == PlsqlTokenId.WHITESPACE && token.toString().contains("\n")) {
  1704                 if (tokenPre != null && tokenPre.id() == PlsqlTokenId.SQL_PLUS) {
  1705                     isSqlPlus = true;
  1706                 }
  1707                 break;
  1708             }
  1709 
  1710             if (token.id() != PlsqlTokenId.WHITESPACE) {
  1711                 tokenPre = token;
  1712             }
  1713         }
  1714 
  1715         if (tokenPre != null && tokenPre.id() == PlsqlTokenId.SQL_PLUS) {
  1716             isSqlPlus = true;
  1717         }
  1718 
  1719         ts.move(offset);
  1720         ts.moveNext();
  1721         return isSqlPlus;
  1722     }
  1723 
  1724     /**
  1725      * Update block hierarchy based on the document event and the action
  1726      */
  1727     private synchronized void updateBlocks(Document doc, List<EventProperties> docList) {
  1728         LOG.log(Level.FINE, "updateBlocks", new Object[]{doc, docList});
  1729         clear();
  1730         try {
  1731             ((AbstractDocument) doc).readLock();
  1732 
  1733             docStartOffset = doc.getStartPosition().getOffset();
  1734             docEndOffset = doc.getEndPosition().getOffset();
  1735 
  1736             EventProperties event = docList.get(0);
  1737             //Area to be re parsed
  1738             startParse = event.offset;
  1739             endParse = event.offset + event.length;
  1740             if (event.mode == DocumentEvent.EventType.REMOVE) {
  1741                 endParse = startParse;
  1742             }
  1743 
  1744             for (int x = 0; x < docList.size(); x++) {
  1745                 event = docList.get(x);
  1746                 LOG.log(Level.FINE, "event={0}, ", new Object[]{event});
  1747                 int offset = event.offset;
  1748                 int length = event.length;
  1749                 DocumentEvent.EventType action = event.mode;
  1750 
  1751                 //get the affected area
  1752                 int startOffset = offset;
  1753                 int endOffset = startOffset + length;
  1754                 if (action == DocumentEvent.EventType.REMOVE) {
  1755                     endOffset = startOffset;
  1756                 }
  1757 
  1758                 //If action is remove, remove code folds in the affected area and repass them
  1759                 if (action == DocumentEvent.EventType.REMOVE || action == DocumentEvent.EventType.INSERT) {
  1760                     if (action == DocumentEvent.EventType.REMOVE) {
  1761                         //Rearrange the offsets of the blocks below the area
  1762                         removeEnclosedBlocks(doc, blockHierarchy, startOffset, startOffset + length);
  1763                         changeOffSet(blockHierarchy, startOffset, -length, true, false);
  1764                         changeOffSet(customFoldBlocks, startOffset, -length, true, false);
  1765                         changeOffSet(toBeRemoved, startOffset, -length, false, true);
  1766                         changedLength = changedLength - length;
  1767                     } else if (action == DocumentEvent.EventType.INSERT) {
  1768                         //Rearrange the offsets of the blocks below the start of the area                  
  1769                         changeOffSet(blockHierarchy, startOffset, length, true, false);
  1770                         changeOffSet(customFoldBlocks, startOffset, length, true, false);
  1771                         changeOffSet(toBeRemoved, startOffset, length, false, true);
  1772                         changedLength = changedLength + length;
  1773                     }
  1774                     //adjust offsets according to the change
  1775                     if (startParse > startOffset) {
  1776                         startParse = startOffset;
  1777                     }
  1778                     if (endParse < endOffset) {
  1779                         endParse = endOffset;
  1780                     }
  1781 
  1782                     int count = blockHierarchy.size();
  1783                     removeBlocksWithin(doc, blockHierarchy, startOffset, endOffset);
  1784                     //If action removeimmediately before block and after block
  1785                     if (count == 0) {//If no blocks pass the whole root
  1786 
  1787                         startParse = docStartOffset;
  1788                         endParse = docEndOffset - 1;
  1789                     } else {
  1790                         if (startParse == startOffset) {//if start offset already adjusted no need to do again
  1791 
  1792                             PlsqlBlock block = null;
  1793                             block = getImmediateBefore(blockHierarchy, block, startOffset);
  1794                             PlsqlBlock parent = getParentBlock(blockHierarchy, startParse, endParse);
  1795 
  1796                             if ((parent != null) && (block != null)) {
  1797                                 if (parent.getStartOffset() > block.getStartOffset()) {
  1798                                     int newStart = getSecondLineOfBlock(doc, parent); // we are not going to remove the parent here
  1799                                     if (newStart < startParse) {
  1800                                         startParse = newStart;
  1801                                     }
  1802                                 } else {
  1803                                     removeBlock(block, parent.getChildBlocks());
  1804                                 }
  1805                             } else {
  1806                                 if (block != null) {
  1807                                     removeBlock(block, blockHierarchy);
  1808                                 } else if (parent != null) { // we are not going to remove the parent here
  1809 
  1810                                     int newStart = getSecondLineOfBlock(doc, parent);
  1811                                     if (newStart < startParse) {
  1812                                         startParse = newStart;
  1813                                     }
  1814                                 } else {
  1815                                     startParse = docStartOffset;
  1816                                 }
  1817                             }
  1818                         }
  1819 
  1820                         if (endParse == endOffset) {//if end offset already adjusted no need to do again
  1821 
  1822                             PlsqlBlock block = null;
  1823                             block = getImmediateAfter(blockHierarchy, block, endOffset);
  1824                             PlsqlBlock parent = getParentBlock(blockHierarchy, startParse, endParse);
  1825 
  1826                             if ((parent != null) && (block != null)) {
  1827                                 if (parent.getEndOffset() < block.getEndOffset()) {
  1828                                     int newEnd = getPreLineOfBlockEnd(doc, parent); // we are not going to remove the parent here
  1829                                     if (newEnd > endParse) {
  1830                                         endParse = newEnd;
  1831                                     }
  1832                                 } else {
  1833                                     removeBlock(block, parent.getChildBlocks());
  1834                                 }
  1835                             } else {
  1836                                 if (block != null) {
  1837                                     removeBlock(block, blockHierarchy);
  1838                                 } else if (parent != null) { // we are not going to remove the parent here
  1839 
  1840                                     int newEnd = getPreLineOfBlockEnd(doc, parent);
  1841                                     if (newEnd > endParse) {
  1842                                         endParse = newEnd;
  1843                                     }
  1844                                 } else {
  1845                                     endParse = docEndOffset - 1;
  1846                                 }
  1847                             }
  1848                         }
  1849 
  1850                         //Remove custom fold blocks that are there within the parse area
  1851                         removeCustomBlocks(customFoldBlocks, startParse, endParse);
  1852 
  1853                         //When files are deleted and written after opening once following can happen
  1854                         if ((endParse < 0) || (endParse > docEndOffset - 1)) {
  1855                             endParse = docEndOffset - 1;
  1856                         }
  1857 
  1858                         if (startParse < 0) {
  1859                             startParse = docStartOffset;
  1860                         }
  1861 
  1862                         if (startParse > docEndOffset - 1) {
  1863                             startParse = docEndOffset - 1;
  1864                         }
  1865                     }
  1866                 } else { //UPDATE pass again
  1867 
  1868                     startParse = docStartOffset;
  1869                     endParse = docEndOffset - 1;
  1870 
  1871                     //clean block hierarchy
  1872                     blockHierarchy.clear();
  1873                     customFoldBlocks.clear();
  1874 
  1875                     //we are going for a reparse, remove all the events for this doc
  1876                     docList.clear();
  1877                     break;
  1878                 }
  1879             }
  1880 
  1881             if (startParse >= endParse) //Can happen in removes
  1882             {
  1883                 return;
  1884             }
  1885 
  1886             //Check whether defines are changed from the affected area
  1887             checkAffected(doc, startParse, endParse);
  1888 
  1889             if (isDefineChanged) {
  1890                 getAliases(doc);
  1891             }
  1892 
  1893             //pass affected section again
  1894             generateBlocks(doc);
  1895 
  1896         } catch (Exception e) {
  1897             ErrorManager.getDefault().notify(e);
  1898         } finally {
  1899             ((AbstractDocument) doc).readUnlock();
  1900         }
  1901     }
  1902 
  1903     /**
  1904      * Get block which is immediately before the given offset
  1905      *
  1906      * @param blocks
  1907      * @param block
  1908      * @param offset
  1909      * @return
  1910      */
  1911     private PlsqlBlock getImmediateBefore(List blocks, PlsqlBlock block, int offset) {
  1912         int count = blocks.size();
  1913 
  1914         for (int i = 0; i < count; i++) {
  1915             PlsqlBlock temp = (PlsqlBlock) blocks.get(i);
  1916             //check from children
  1917             PlsqlBlock child = getImmediateBefore(temp.getChildBlocks(), block, offset);
  1918             if (child != null) {
  1919                 block = child;
  1920             }
  1921 
  1922             if ((temp.getEndOffset() < offset)
  1923                     && ((block == null) || (block.getEndOffset() < temp.getEndOffset()))) {
  1924                 block = temp;
  1925             }
  1926         }
  1927 
  1928         return block;
  1929     }
  1930 
  1931     /**
  1932      * Get fold which is immediately after the given offset
  1933      *
  1934      * @param blocks
  1935      * @param block
  1936      * @param offset
  1937      * @return
  1938      */
  1939     private PlsqlBlock getImmediateAfter(List<PlsqlBlock> blocks, PlsqlBlock block, int offset) {
  1940         int count = blocks.size();
  1941 
  1942         for (int i = 0; i < count; i++) {
  1943             PlsqlBlock temp = blocks.get(i);
  1944             //check from children
  1945             PlsqlBlock child = getImmediateAfter(temp.getChildBlocks(), block, offset);
  1946             if (child != null) {
  1947                 block = child;
  1948             }
  1949 
  1950             if ((temp.getStartOffset() > offset)
  1951                     && ((block == null) || (block.getStartOffset() > temp.getStartOffset()))) {
  1952                 block = temp;
  1953             }
  1954         }
  1955 
  1956         return block;
  1957     }
  1958 
  1959     /**
  1960      * Method that will generate blocks of the given offset range
  1961      *
  1962      * @param startOffset
  1963      * @param endOffset
  1964      */
  1965     private synchronized void generateBlocks(Document doc) {
  1966         List<PlsqlBlock> immediateBlockHier;
  1967         tokenHierarchy = TokenHierarchy.get(doc);
  1968         @SuppressWarnings("unchecked")
  1969         TokenSequence<PlsqlTokenId> ts = tokenHierarchy.tokenSequence(PlsqlTokenId.language());
  1970         if (ts == null) {
  1971             return;
  1972         }
  1973 
  1974         PlsqlBlock parent = getParentBlock(blockHierarchy, startParse, endParse);
  1975         if (parent != null) {
  1976             immediateBlockHier = parent.getChildBlocks();
  1977         } else {
  1978             immediateBlockHier = blockHierarchy;
  1979         }
  1980 
  1981         LOG.log(Level.FINE, "generateBlocks, doc.getLength()={0}, ts.tokenCount()={1}",
  1982                 new Object[]{doc.getLength(), ts.tokenCount()});
  1983         //move offset
  1984         ts.move(startParse);
  1985         Token<PlsqlTokenId> tempToken;
  1986         Token<PlsqlTokenId> customStartToken = null;
  1987         Token<PlsqlTokenId> customEndToken = null;
  1988 
  1989         //Go through all the available tokens
  1990         while (ts.moveNext()) {
  1991             tempToken = ts.token();
  1992             PlsqlTokenId tokenID = tempToken.id();
  1993             String image = tempToken.text().toString();
  1994 
  1995             LOG.log(Level.FINE, "tempToken.id()={0}, tempToken.text()={1}",
  1996                     new Object[]{tempToken.id(), tempToken.text()});
  1997 
  1998             //Check end offset and break (exception for colun which will mark end of some blocks)
  1999             if ((!image.equals(";")) && (tempToken.offset(tokenHierarchy) > endParse)) {
  2000                 break;
  2001             }
  2002 
  2003             if (tokenID == PlsqlTokenId.KEYWORD) {
  2004                 if (image.equalsIgnoreCase("VIEW")) {
  2005                     int offset = ts.offset();
  2006                     PlsqlBlock block = checkView(tempToken, ts, immediateBlockHier);
  2007                     if (block == null) {
  2008                         //pass from the immediate next token to get inner blocks
  2009                         ts.move(offset);
  2010                         ts.moveNext();
  2011                     } else {
  2012                         checkAndAddNew(block, parent, immediateBlockHier);
  2013                     }
  2014                 } else if (image.equalsIgnoreCase("FUNCTION")) {
  2015                     int offset = ts.offset();
  2016                     PlsqlBlock block = checkMethod(tempToken, ts, PlsqlBlockType.FUNCTION_IMPL, immediateBlockHier);
  2017                     if (block == null) {
  2018                         //pass from the immediate next token to get inner blocks
  2019                         ts.move(offset);
  2020                         ts.moveNext();
  2021                     } else {
  2022                         checkAndAddNew(block, parent, immediateBlockHier);
  2023                     }
  2024                 } else if (image.equalsIgnoreCase("PROCEDURE")) {
  2025                     int offset = ts.offset();
  2026                     PlsqlBlock block = checkMethod(tempToken, ts, PlsqlBlockType.PROCEDURE_IMPL, immediateBlockHier);
  2027                     if (block == null) {
  2028                         //pass from the immediate next token to get inner blocks
  2029                         ts.move(offset);
  2030                         ts.moveNext();
  2031                     } else {
  2032                         checkAndAddNew(block, parent, immediateBlockHier);
  2033                     }
  2034                 } else if (image.equalsIgnoreCase("PACKAGE")) {
  2035                     int offset = ts.offset();
  2036                     PlsqlBlock block = checkPackage(tempToken, ts, immediateBlockHier);
  2037                     if (block == null) {
  2038                         //pass from the immediate next token to get inner blocks
  2039                         ts.move(offset);
  2040                         ts.moveNext();
  2041                     } else {
  2042                         checkAndAddNew(block, parent, immediateBlockHier);
  2043                     }
  2044                 } else if (image.equalsIgnoreCase("CURSOR")) {
  2045                     int offset = ts.offset();
  2046                     PlsqlBlock block = checkCursor(tempToken, ts, immediateBlockHier);
  2047                     if (block == null) {
  2048                         //pass from the immediate next token to get inner blocks
  2049                         ts.move(offset);
  2050                         ts.moveNext();
  2051                     } else {
  2052                         checkAndAddNew(block, parent, immediateBlockHier);
  2053                     }
  2054                 } else if (image.equalsIgnoreCase("TRIGGER")) {
  2055                     int offset = ts.offset();
  2056                     PlsqlBlock block = checkTrigger(tempToken, ts, immediateBlockHier);
  2057                     if (block == null) {
  2058                         //pass from the immediate next token to get inner blocks
  2059                         ts.move(offset);
  2060                         ts.moveNext();
  2061                     } else {
  2062                         checkAndAddNew(block, parent, immediateBlockHier);
  2063                     }
  2064                 } else if (image.equalsIgnoreCase("COMMENT")) {
  2065                     int offset = ts.offset();
  2066                     PlsqlBlock block = checkTblColComment(tempToken, ts, immediateBlockHier);
  2067                     if (block == null) {
  2068                         //pass from the immediate next token to get inner blocks
  2069                         ts.move(offset);
  2070                         ts.moveNext();
  2071                     } else {
  2072                         checkAndAddNew(block, parent, immediateBlockHier);
  2073                     }
  2074                 } else if (image.equalsIgnoreCase("DECLARE")) {
  2075                     PlsqlBlock block = checkDeclareBlock(tempToken, ts, immediateBlockHier);
  2076                     if (block != null) {//If inner check seems to have failed need to continue this one
  2077                         checkAndAddNew(block, parent, immediateBlockHier);
  2078                     }
  2079                 } else if (image.equalsIgnoreCase("BEGIN")) {
  2080                     if (!isDeclare(ts, immediateBlockHier)) {//We need to check whether the declare is isolated by a CURSOR block
  2081 
  2082                         int offset = ts.offset();
  2083                         PlsqlBlock block = checkBeginBlock(tempToken, ts, immediateBlockHier);
  2084                         if (block == null) {//If inner check seems to have failed need to continue this one
  2085 
  2086                             ts.move(offset);
  2087                             ts.moveNext();
  2088                         } else {
  2089                             checkAndAddNew(block, parent, immediateBlockHier);
  2090                         }
  2091                     }
  2092                 } else if (image.equalsIgnoreCase("IF")
  2093                         || image.equalsIgnoreCase("ELSIF")) {
  2094                     if (!isNotBlockStart(tempToken, ts)) {
  2095                         int offset = tempToken.offset(tokenHierarchy);
  2096                         List children = checkIfBlock(tempToken, ts, immediateBlockHier);
  2097                         if (children == null || children.isEmpty()) {//If inner check seems to have failed need to continue this one
  2098 
  2099                             ts.move(offset);
  2100                             ts.moveNext();
  2101                         } else {
  2102                             for (int i = 0; i < children.size(); i++) {
  2103                                 PlsqlBlock child = (PlsqlBlock) children.get(i);
  2104                                 checkAndAddNew(child, parent, immediateBlockHier);
  2105                             }
  2106                         }
  2107                     }
  2108                 } else if (image.equalsIgnoreCase("ELSE")) {
  2109                     if (!isNotBlockStart(tempToken, ts)) {
  2110                         int offset = tempToken.offset(tokenHierarchy);
  2111                         List children = checkIfBlock(tempToken, ts, immediateBlockHier);
  2112                         if (children == null || children.isEmpty()) {
  2113                             children = checkCaseBlock(tempToken, ts, immediateBlockHier, false);
  2114                         }
  2115 
  2116                         if (children == null || children.isEmpty()) {//If inner check seems to have failed need to continue this one
  2117 
  2118                             ts.move(offset);
  2119                             ts.moveNext();
  2120                         } else {
  2121                             for (int i = 0; i < children.size(); i++) {
  2122                                 PlsqlBlock child = (PlsqlBlock) children.get(i);
  2123                                 checkAndAddNew(child, parent, immediateBlockHier);
  2124                             }
  2125                         }
  2126                     }
  2127                 } else if (image.equalsIgnoreCase("CASE")
  2128                         || image.equalsIgnoreCase("WHEN")) {
  2129                     if (!isNotBlockStart(tempToken, ts)) {
  2130                         int offset = tempToken.offset(tokenHierarchy);
  2131                         List children = checkCaseBlock(tempToken, ts, immediateBlockHier, false);
  2132                         if (children == null || children.isEmpty()) {//If inner check seems to have failed need to continue this one
  2133 
  2134                             ts.move(offset);
  2135                             ts.moveNext();
  2136                         } else {
  2137                             for (int i = 0; i < children.size(); i++) {
  2138                                 PlsqlBlock child = (PlsqlBlock) children.get(i);
  2139                                 checkAndAddNew(child, parent, immediateBlockHier);
  2140                             }
  2141                         }
  2142                     }
  2143                 } else if (image.equalsIgnoreCase("LOOP")
  2144                         || image.equalsIgnoreCase("WHILE")
  2145                         || image.equalsIgnoreCase("FOR")) {
  2146                     if (!isNotBlockStart(tempToken, ts)) {
  2147                         int offset = tempToken.offset(tokenHierarchy);
  2148                         if (!unsuccessBlocks.contains(offset)) {
  2149                             PlsqlBlock child = checkLoopBlock(tempToken, ts, immediateBlockHier);
  2150                             if (child == null) {//If inner check seems to have failed need to continue this one
  2151                                 unsuccessBlocks.add(offset);
  2152                                 ts.move(offset);
  2153                                 ts.moveNext();
  2154                             } else {
  2155                                 checkAndAddNew(child, parent, immediateBlockHier);
  2156                             }
  2157                         }
  2158                     }
  2159                 } else if (image.equalsIgnoreCase("TABLE")
  2160                         || image.equalsIgnoreCase("INDEX")
  2161                         || image.equalsIgnoreCase("SELECT")
  2162                         || image.equalsIgnoreCase("UPDATE")
  2163                         || image.equalsIgnoreCase("DELETE")
  2164                         || image.equalsIgnoreCase("INSERT")
  2165                         || image.equalsIgnoreCase("MERGE")
  2166                         || image.equalsIgnoreCase("DROP")
  2167                         || image.equalsIgnoreCase("SEQUENCE")) {
  2168                     if (!isNotBlockStart(tempToken, ts)) {
  2169                         int offset = tempToken.offset(tokenHierarchy);
  2170                         PlsqlBlock child = checkStatementBlock(tempToken, ts, immediateBlockHier);
  2171                         if (child == null) {//If inner check seems to have failed need to continue this one
  2172 
  2173 //                            ts.move(offset);
  2174 //                            ts.moveNext();
  2175                         } else {
  2176                             checkAndAddNew(child, parent, immediateBlockHier);
  2177                         }
  2178                     }
  2179                 }
  2180             } else if (tokenID == PlsqlTokenId.JAVA_SOUCE) {
  2181                 int offset = ts.offset();
  2182                 PlsqlBlock block = null;
  2183                 block = checkJavaSource(tempToken, ts);
  2184                 if (block == null) {
  2185                     //pass from the immediate next token to get inner blocks
  2186                     ts.move(offset);
  2187                     ts.moveNext();
  2188                 } else {
  2189                     checkAndAddNew(block, parent, immediateBlockHier);
  2190                 }
  2191             } else if (tokenID == PlsqlTokenId.LINE_COMMENT) {
  2192                 //only single comment line
  2193                 if (image.toUpperCase(Locale.ENGLISH).contains("<FOLD>")) {
  2194                     customStartToken = tempToken;
  2195                 } else if (image.toUpperCase(Locale.ENGLISH).contains("<END-FOLD>")) {
  2196                     if (customStartToken != null) {
  2197                         String name = customStartToken.text().toString();
  2198                         int index = name.toUpperCase(Locale.ENGLISH).indexOf("<FOLD>");
  2199                         name = name.substring(index + 7).trim();
  2200                         if (ts.moveNext()) {
  2201                             tempToken = ts.token();
  2202                             PlsqlBlock custom = new PlsqlBlock(customStartToken.offset(tokenHierarchy),
  2203                                     tempToken.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
  2204                             customFoldBlocks.add(custom);
  2205                         }
  2206                         customStartToken = null;
  2207                     } else {
  2208                         customEndToken = tempToken;
  2209                     }
  2210                 } else {
  2211                     PlsqlBlock block = checkComment(tempToken, ts);
  2212                     if (block != null) {
  2213                         checkAndAddNew(block, parent, immediateBlockHier);
  2214                     }
  2215                 }
  2216             } else if (tokenID == PlsqlTokenId.BLOCK_COMMENT) {
  2217                 int start = tempToken.offset(tokenHierarchy);
  2218                 PlsqlBlock block = new PlsqlBlock(start,
  2219                         start + tempToken.length(), "BLOCK COMMENT", "", PlsqlBlockType.COMMENT);
  2220                 if (block != null) {
  2221                     checkAndAddNew(block, parent, immediateBlockHier);
  2222                 }
  2223             } else if ((tokenID == PlsqlTokenId.OPERATOR) && (image.equals(";"))) {
  2224                 PlsqlBlock block = checkEnd(tempToken, ts);
  2225                 //check whether this is the parent can happen in a remove
  2226                 if (block != null && (block.getType() != PlsqlBlockType.FUNCTION_IMPL && block.getType() != PlsqlBlockType.PROCEDURE_IMPL)) {
  2227                     if (!isEqual(parent, block)) {
  2228                         if ((block != null) && (checkExisting(block, immediateBlockHier) == false)) {
  2229                             addImmediateChildren(block, immediateBlockHier);
  2230                             immediateBlockHier.add(block);
  2231                             newBlocks.add(block);
  2232                             if (parent != null) {
  2233                                 block.setParent(parent);
  2234                             }
  2235                         }
  2236                     }
  2237                 }
  2238             }
  2239         }   //we have come to the end now, check whether we have unmatched custom tokens
  2240 
  2241         if (customEndToken != null) {
  2242             checkCustom(customEndToken, ts, immediateBlockHier, parent, "END");
  2243         } else if (customStartToken != null) {
  2244             checkCustom(customStartToken, ts, immediateBlockHier, parent, "START");
  2245         }
  2246     }
  2247 
  2248     private void checkAndAddNew(PlsqlBlock block, PlsqlBlock parent, List<PlsqlBlock> immediateBlockHier) {
  2249         LOG.log(Level.FINE, "checkAndAddNew, block.getName()={0}, block.getType()={1}", new Object[]{block.getName(), block.getType()});
  2250         if (checkExisting(block, immediateBlockHier) == false && !isEqual(parent, block)) {
  2251             immediateBlockHier.add(block);
  2252             LOG.log(Level.FINE, "newBlocks.add({0})", new Object[]{block.getName(), block.getType()});
  2253             newBlocks.add(block);
  2254             if (parent != null) {
  2255                 block.setParent(parent);
  2256             }
  2257         }
  2258     }
  2259 
  2260     /**
  2261      * Check whether current ';' is the end of a function/procedure/view/package
  2262      *
  2263      * @param tempToken
  2264      * @param ts
  2265      */
  2266     private PlsqlBlock checkEnd(Token<PlsqlTokenId> endToken, TokenSequence<PlsqlTokenId> ts) {
  2267         Token<PlsqlTokenId> end = endToken; //means ';' here
  2268 
  2269         Token<PlsqlTokenId> begin = null;
  2270         String methodName = "";
  2271         int type = -1; //1 for FUNCTION/PROCEDURE/PACKAGE
  2272 
  2273         boolean moveBack = false;
  2274         PlsqlBlock block = null;
  2275         moveBack = getPreviousNonWhitespace(ts, true);
  2276         Token<PlsqlTokenId> tmp = ts.token();
  2277 
  2278         if (moveBack == false) {
  2279             ts.move(end.offset(tokenHierarchy));
  2280             ts.moveNext();
  2281             return block;
  2282         }
  2283 
  2284         //If this is a function/procedure end take the name
  2285         if ((tmp.id() == PlsqlTokenId.KEYWORD) && (tmp.text().toString().equalsIgnoreCase("END"))) {
  2286             type = 0;
  2287         } else if (tmp.id() == PlsqlTokenId.IDENTIFIER || tmp.id() == PlsqlTokenId.KEYWORD) {
  2288             methodName = tmp.text().toString();
  2289 
  2290             //Check whether this is an 'end <ide>;'
  2291             moveBack = getPreviousNonWhitespace(ts, true);
  2292             tmp = ts.token();
  2293 
  2294             if (moveBack == false) {
  2295                 ts.move(end.offset(tokenHierarchy));
  2296                 ts.moveNext();
  2297                 return block;
  2298             }
  2299 
  2300             if ((tmp.id() == PlsqlTokenId.KEYWORD)
  2301                     && (tmp.text().toString().equalsIgnoreCase("END"))) {
  2302                 type = 1;
  2303             }
  2304 
  2305         }
  2306 
  2307         if (type == 1) {
  2308             begin = checkMethodBegin(ts, methodName);
  2309         } else if (type == 0) {
  2310             begin = checkMethodBegin(ts);
  2311 
  2312             Token<PlsqlTokenId> nameToken = begin;
  2313             //get name
  2314             if (getNextNonWhitespace(ts, true)) {
  2315                 nameToken = ts.token();
  2316                 if (!nameToken.text().toString().equalsIgnoreCase("BODY")) {
  2317                     methodName = nameToken.text().toString();
  2318                 } else {
  2319                     if (getNextNonWhitespace(ts, true)) {
  2320                         nameToken = ts.token();
  2321                         methodName = nameToken.text().toString();
  2322                     }
  2323                 }
  2324             }
  2325         }
  2326 
  2327         //Cold block is found
  2328         if (begin != null) {
  2329             String image = begin.text().toString();
  2330             String alias = "";
  2331             if (methodName.indexOf('&') != -1) {
  2332                 alias = methodName;
  2333             }
  2334 
  2335             methodName = getDefine(methodName);
  2336 
  2337             //Get the token after the end token
  2338             ts.move(end.offset(tokenHierarchy));
  2339             ts.moveNext();
  2340             ts.moveNext();
  2341 
  2342             if (image.equalsIgnoreCase("PROCEDURE")) {
  2343                 block = new PlsqlBlock(begin.offset(tokenHierarchy), ts.offset(),
  2344                         methodName, alias, PlsqlBlockType.PROCEDURE_IMPL);
  2345                 checkPrefix(begin.offset(tokenHierarchy), ts, block);
  2346             } else if (image.equalsIgnoreCase("PACKAGE")) {
  2347                 //get next token & check
  2348                 //find semicolon
  2349                 int endPos = ts.offset();
  2350                 ts.move(begin.offset(tokenHierarchy));
  2351                 ts.moveNext();
  2352                 boolean moveNext = false;
  2353                 moveNext = getNextNonWhitespace(ts, true);
  2354                 Token<PlsqlTokenId> next = ts.token();
  2355                 PlsqlBlockType foldType = PlsqlBlockType.PACKAGE;
  2356                 if ((moveNext != false) && (next.text().toString().equalsIgnoreCase("BODY"))) {
  2357                     foldType = PlsqlBlockType.PACKAGE_BODY;
  2358                 }
  2359 
  2360                 block = new PlsqlBlock(begin.offset(tokenHierarchy), endPos,
  2361                         methodName, alias, foldType);
  2362                 checkPrefix(begin.offset(tokenHierarchy), ts, block);
  2363             } else {
  2364                 block = new PlsqlBlock(begin.offset(tokenHierarchy), ts.offset(),
  2365                         methodName, alias, PlsqlBlockType.FUNCTION_IMPL);
  2366                 checkPrefix(begin.offset(tokenHierarchy), ts, block);
  2367             }
  2368         }
  2369 
  2370         ts.move(end.offset(tokenHierarchy));
  2371         ts.moveNext();
  2372         return block;
  2373     }
  2374 
  2375     /**
  2376      * Check whether there is a function/procedure in this block
  2377      *
  2378      * @param ts
  2379      * @param methodName
  2380      * @return
  2381      */
  2382     private Token<PlsqlTokenId> checkMethodBegin(TokenSequence<PlsqlTokenId> ts, String methodName) {
  2383         Token<PlsqlTokenId> begin = null;
  2384         boolean moveBack = ts.movePrevious();
  2385         Token<PlsqlTokenId> tmp = ts.token();
  2386         int endCount = 0;
  2387 
  2388         while (moveBack) {
  2389             String image = tmp.text().toString();
  2390 
  2391             if (image.equalsIgnoreCase(methodName)) {
  2392                 //Go to previous word
  2393                 boolean move = getPreviousNonWhitespace(ts, true);
  2394                 Token<PlsqlTokenId> token = ts.token();
  2395 
  2396                 if ((move != false) && (token.id() == PlsqlTokenId.KEYWORD)) {
  2397                     if ((token.text().toString().equalsIgnoreCase("FUNCTION"))
  2398                             || (token.text().toString().equalsIgnoreCase("PROCEDURE"))) {
  2399                         //if there were inner functions & procedures
  2400                         if (endCount != 0) {
  2401                             endCount--;
  2402                         } else {
  2403                             return token;
  2404                         }
  2405                     } else if (token.text().toString().equalsIgnoreCase("BODY")) {
  2406                         boolean pre = getPreviousNonWhitespace(ts, true);
  2407                         Token<PlsqlTokenId> previous = ts.token();
  2408                         if ((pre != false) && (previous.text().toString().equalsIgnoreCase("PACKAGE"))) {
  2409                             return previous;
  2410                         }
  2411 
  2412                     } else if (token.text().toString().equalsIgnoreCase("END")) {
  2413                         ++endCount;
  2414                     }
  2415                 }
  2416             }
  2417             moveBack = getPreviousNonWhitespace(ts, true);
  2418             tmp = ts.token();
  2419         }
  2420 
  2421         return begin;
  2422     }
  2423 
  2424     /**
  2425      * Check whether there is a function/procedure in this block Method name is not there in the 'END;'
  2426      *
  2427      * @param ts
  2428      * @return
  2429      */
  2430     private Token<PlsqlTokenId> checkMethodBegin(TokenSequence<PlsqlTokenId> ts) {
  2431         Token<PlsqlTokenId> begin = null;
  2432         boolean moveBack = ts.movePrevious();
  2433         Token<PlsqlTokenId> tmp = ts.token();
  2434         int endCount = 0;
  2435 
  2436         while (moveBack) {
  2437             String image = tmp.text().toString();
  2438 
  2439             if (tmp.id() == PlsqlTokenId.KEYWORD) {
  2440                 if ((image.equalsIgnoreCase("BEGIN"))) {
  2441                     endCount--;
  2442                 } else if ((image.equalsIgnoreCase("END"))) {
  2443                     int off = ts.offset();
  2444                     if (getNextNonWhitespace(ts, true)) {
  2445                         tmp = ts.token();
  2446                     }
  2447 
  2448                     if ((tmp.text().toString().equals(";")) || (tmp.id() == PlsqlTokenId.IDENTIFIER)
  2449                             || (tmp.id() == PlsqlTokenId.KEYWORD && (!tmp.toString().equalsIgnoreCase("IF")
  2450                             && !tmp.toString().equalsIgnoreCase("CASE") && !tmp.toString().equalsIgnoreCase("LOOP")))) {
  2451                         endCount++;
  2452                     }
  2453 
  2454                     ts.move(off);
  2455                     ts.moveNext();
  2456                 } else if ((endCount == 0) & (image.equalsIgnoreCase("PACKAGE"))) {
  2457                     return tmp;
  2458                 } else if ((endCount == -1) && ((image.equalsIgnoreCase("PROCEDURE"))
  2459                         || (image.equalsIgnoreCase("FUNCTION")))) {
  2460                     return tmp;
  2461                 } else if ((endCount == 0) & (image.equalsIgnoreCase("BODY"))) {
  2462                     boolean pre = getPreviousNonWhitespace(ts, true);
  2463                     Token<PlsqlTokenId> previous = ts.token();
  2464                     if ((pre != false) && (previous.text().toString().equalsIgnoreCase("PACKAGE"))) {
  2465                         return previous;
  2466                     }
  2467                 }
  2468             }
  2469 
  2470             moveBack = getPreviousNonWhitespace(ts, true);
  2471             tmp = ts.token();
  2472         }
  2473 
  2474         return begin;
  2475     }
  2476 
  2477     /**
  2478      * Check whether this is a block comment, single line or multi lined comment
  2479      *
  2480      * @param current
  2481      * @param ts
  2482      * @return
  2483      */
  2484     private PlsqlBlock checkComment(Token<PlsqlTokenId> current, TokenSequence<PlsqlTokenId> ts) {
  2485         LOG.log(Level.FINE, "checkComment, ts.index()={0} current={1} ", new Object[]{ts.index(), current});
  2486         //If the line don't start with the comment ignore
  2487         String prefix = getPreceedingText(current.offset(tokenHierarchy), ts);
  2488         if (!prefix.trim().equals("")) {
  2489             return null;
  2490         }
  2491 
  2492         Token<PlsqlTokenId> commentBegin = current;
  2493         Token<PlsqlTokenId> commentEnd = current;
  2494         String text = commentBegin.text().toString();
  2495         boolean moveNext = getNextNonWhitespaceForComments(ts);
  2496         Token<PlsqlTokenId> tmp = ts.token();
  2497         boolean takeDesc = true;
  2498         String desc = getCommentDescription(text);
  2499         if (!desc.equals("COMMENT...")) {
  2500             takeDesc = false;
  2501         }
  2502 
  2503         while (moveNext) {
  2504             PlsqlTokenId tokenID = tmp.id();
  2505 
  2506             //We have come to the end of the view declaration
  2507             if (tokenID != PlsqlTokenId.LINE_COMMENT) {
  2508                 break;
  2509             } else {
  2510                 commentEnd = tmp;
  2511                 if (takeDesc) {
  2512                     text = text + tmp.text().toString();
  2513                     desc = getCommentDescription(text);
  2514                     if (!desc.equals("COMMENT...")) {
  2515                         takeDesc = false;
  2516                     }
  2517                 }
  2518                 moveNext = getNextNonWhitespaceForComments(ts);
  2519                 tmp = ts.token();
  2520             }
  2521         }
  2522 
  2523         ts.move(commentEnd.offset(tokenHierarchy));
  2524         ts.moveNext();
  2525 
  2526         //Calculate end offset
  2527         if(commentEnd.id() == PlsqlTokenId.WHITESPACE ){
  2528             ts.movePrevious();
  2529             commentEnd = ts.token();
  2530         }
  2531         int endOffset = commentEnd.offset(tokenHierarchy) + commentEnd.length();
  2532 
  2533         return new PlsqlBlock(commentBegin.offset(tokenHierarchy),
  2534                 endOffset, desc, "", PlsqlBlockType.COMMENT);
  2535     }
  2536 
  2537     /**
  2538      * Method that will give the description of the comment fold
  2539      *
  2540      * @param text
  2541      * @return
  2542      */
  2543     private String getCommentDescription(String text) {
  2544         String description = "COMMENT...";
  2545 
  2546         //Get first -- character from begin to end
  2547         char[] textArr = text.toCharArray();
  2548         int begin = 0;
  2549         int end = 0;
  2550 
  2551         //Get the start character which is not -
  2552         int i = 0;
  2553         while (textArr.length > i) {
  2554             if ((textArr[i] != '-') && (textArr[i] != ' ')) {
  2555                 begin = i;
  2556                 break;
  2557             }
  2558             i++;
  2559         }
  2560 
  2561         //Get end character which is -
  2562         i++;
  2563         while (textArr.length > i) {
  2564             if (textArr[i] == '-') {
  2565                 break;
  2566             }
  2567             i++;
  2568         }
  2569 
  2570         end = i;
  2571 
  2572         if (begin != 0) {
  2573             description = "-- " + text.substring(begin, end);
  2574         }
  2575 
  2576         return description;
  2577     }
  2578 
  2579     /**
  2580      * Check whether this is the start of a PROCEDURE/FUNCTION block
  2581      *
  2582      * @param methodToken
  2583      * @param ts
  2584      * @param type
  2585      * @return
  2586      */
  2587     private PlsqlBlock checkMethod(Token<PlsqlTokenId> methodToken, TokenSequence<PlsqlTokenId> ts, PlsqlBlockType type, List<PlsqlBlock> parentBlocks) {
  2588         Token<PlsqlTokenId> methodBegin = methodToken;
  2589         Token<PlsqlTokenId> tmp = methodToken;
  2590         PlsqlBlock block = null;
  2591         List<PlsqlBlock> lstChild = new ArrayList<PlsqlBlock>();
  2592         boolean isFound = false;
  2593         boolean pragmaFound = false;
  2594         int colunCount = 0;
  2595         String methodName = "";
  2596         boolean moveNext = false;
  2597         int beginCount = 0; //workaround to identify end of method...
  2598 
  2599         //Check whether the beginning is in a SQL Plus command
  2600         if (sqlPlusLine(ts)) {
  2601             return null;
  2602         }
  2603 
  2604         //Get procedure/function name which is the next non whitespace token
  2605         moveNext = getNextNonWhitespace(ts, true);
  2606         tmp = ts.token();
  2607         if (moveNext == false) {
  2608             return block;
  2609         }
  2610 
  2611         methodName = tmp.text().toString();
  2612         methodName = checkForOtherSchema(ts, methodName);
  2613         String alias = "";
  2614         if (methodName.indexOf('&') != -1) {
  2615             alias = methodName;
  2616         }
  2617 
  2618         methodName = getDefine(methodName);
  2619         Token<PlsqlTokenId> customStartToken = null;
  2620         Token<PlsqlTokenId> previous = tmp;
  2621 
  2622         //Check whether there is the keyword 'IS' before ';'
  2623         while (moveNext) {
  2624             String image = tmp.text().toString();
  2625             PlsqlTokenId tokenID = tmp.id();
  2626 
  2627             if ((tmp != null) && (!image.equals(";")) && (!image.equalsIgnoreCase("END")) && (!previous.toString().equalsIgnoreCase("END")) && (tmp.offset(tokenHierarchy) > endParse)) { //end is added here for the abnormal case in code templates
  2628                 break;
  2629             }
  2630 
  2631             //Increment colun count, if IS is not found before first break
  2632             if ((tokenID == PlsqlTokenId.OPERATOR) && image.equals(";")) {
  2633                 ++colunCount;
  2634                 boolean isEndFound = false;
  2635                 int offset = ts.offset();
  2636                 boolean preMove = false;
  2637                 preMove = getPreviousNonWhitespace(ts, true);
  2638 
  2639                 //workaround for issue with functions/procedures ending with End; (no name)
  2640                 Token<PlsqlTokenId> previousNWS = ts.token();
  2641                 PlsqlTokenId previd = previousNWS.id();
  2642                 if ((preMove != false) && previousNWS.text().toString().equalsIgnoreCase("END")) {
  2643                     if (beginCount < 1) {
  2644                         isEndFound = true;
  2645                     }
  2646                 } else {
  2647                     preMove = getPreviousNonWhitespace(ts, true);
  2648                     previousNWS = ts.token();
  2649                     if ((previd == PlsqlTokenId.IDENTIFIER || previd == PlsqlTokenId.KEYWORD)
  2650                             && previousNWS.text().toString().equalsIgnoreCase("END")) {
  2651                         isEndFound = true;
  2652                     }
  2653                 }
  2654 
  2655                 ts.move(offset);
  2656                 if ((colunCount == 1) && (!isFound) && (!pragmaFound) && (!isEndFound)) {
  2657                     //Although we were looking for impl's we have found a def here
  2658                     ts.moveNext();
  2659                     ts.moveNext();
  2660                     if (type == PlsqlBlockType.PROCEDURE_IMPL) {
  2661                         block = new PlsqlBlock(methodBegin.offset(tokenHierarchy),
  2662                                 ts.offset(), methodName, alias, PlsqlBlockType.PROCEDURE_DEF);
  2663                     } else {
  2664                         block = new PlsqlBlock(methodBegin.offset(tokenHierarchy),
  2665                                 ts.offset(), methodName, alias, PlsqlBlockType.FUNCTION_DEF);
  2666                     }
  2667 
  2668                     return block;
  2669                 }
  2670                 ts.moveNext();
  2671             }
  2672 
  2673             //We might have come to the end of the procedure/function declaration
  2674             if (((tokenID == PlsqlTokenId.OPERATOR) && image.equals(";")) && isFound) {
  2675                 //check whether previous Non white space token to the identifier is END
  2676                 int offset = ts.offset();
  2677                 boolean preMove = false;
  2678                 preMove = getPreviousNonWhitespace(ts, true);
  2679                 //workaround for issue with functions/procedures ending with End; (no name)
  2680                 Token<PlsqlTokenId> previousNWS = ts.token();
  2681                 String prevText = previousNWS.text().toString();
  2682                 if ((preMove != false) && prevText.equalsIgnoreCase("END")) {
  2683                     if (beginCount <= 0) {
  2684                         ts.move(offset);
  2685                         moveNext = ts.moveNext();
  2686                         moveNext = ts.moveNext();
  2687                         block = new PlsqlBlock(methodBegin.offset(tokenHierarchy),
  2688                                 ts.offset(), methodName, alias, type);
  2689                         checkPrefix(methodBegin.offset(tokenHierarchy), ts, block);
  2690                         break;
  2691                     }
  2692                 } else {
  2693                     preMove = getPreviousNonWhitespace(ts, true);
  2694                     previousNWS = ts.token();
  2695                     if (previousNWS.text().toString().equalsIgnoreCase("END")) {
  2696                         if (beginCount <= 0) {
  2697                             ts.move(offset);
  2698                             moveNext = ts.moveNext();
  2699                             moveNext = ts.moveNext();
  2700                             block = new PlsqlBlock(methodBegin.offset(tokenHierarchy),
  2701                                     ts.offset(), methodName, alias, type);
  2702                             checkPrefix(methodBegin.offset(tokenHierarchy), ts, block);
  2703                             break;
  2704                         }
  2705                     }
  2706                 }
  2707                 ts.move(offset);
  2708                 moveNext = ts.moveNext();
  2709             } else if ((tokenID == PlsqlTokenId.KEYWORD)
  2710                     && ((image.equalsIgnoreCase("PROCEDURE"))
  2711                     || (image.equalsIgnoreCase("FUNCTION"))
  2712                     || (image.equalsIgnoreCase("CURSOR")))) {
  2713                 if (isFound && beginCount <= 0) {
  2714                     int beforeOff = tmp.offset(tokenHierarchy);
  2715 
  2716                     if (image.equalsIgnoreCase("PROCEDURE")) {
  2717                         PlsqlBlock child = checkMethod(tmp, ts, PlsqlBlockType.PROCEDURE_IMPL, lstChild);
  2718                         if (child == null) {//If inner check seems to have failed need to continue this one
  2719 
  2720                             ts.move(beforeOff);
  2721                             moveNext = ts.moveNext();
  2722                         } else {
  2723                             if (checkExisting(child, lstChild) == false) {
  2724                                 lstChild.add(child);
  2725                             }
  2726                         }
  2727                     } //Inner procedure
  2728                     else if (image.equalsIgnoreCase("FUNCTION")) {
  2729                         PlsqlBlock child = checkMethod(tmp, ts, PlsqlBlockType.FUNCTION_IMPL, lstChild);
  2730                         if (child == null) {//If inner check seems to have failed need to continue this one
  2731 
  2732                             ts.move(beforeOff);
  2733                             moveNext = ts.moveNext();
  2734                         } else {
  2735                             if (checkExisting(child, lstChild) == false) {
  2736                                 lstChild.add(child);
  2737                             }
  2738                         }
  2739                     } //Inner function
  2740                     else if (image.equalsIgnoreCase("CURSOR")) {
  2741                         PlsqlBlock child = checkCursor(tmp, ts, lstChild);
  2742                         if (child == null) {//If inner check seems to have failed need to continue this one
  2743 
  2744                             ts.move(beforeOff);
  2745                             moveNext = ts.moveNext();
  2746                         } else {
  2747                             if (checkExisting(child, lstChild) == false) {
  2748                                 lstChild.add(child);
  2749                             }
  2750                         }
  2751                     } //Inner cursor
  2752                 } else {
  2753                     break;
  2754                 }
  2755             } else if ((image.equalsIgnoreCase("IF")) && isFound) {
  2756                 int beforeOff = tmp.offset(tokenHierarchy);
  2757                 List children = checkIfBlock(tmp, ts, lstChild);
  2758                 if (children == null || children.isEmpty()) {//If inner check seems to have failed need to continue this one
  2759 
  2760                     ts.move(beforeOff);
  2761                     moveNext =
  2762                             ts.moveNext();
  2763                 } else {
  2764                     for (int i = 0; i
  2765                             < children.size(); i++) {
  2766                         PlsqlBlock child = (PlsqlBlock) children.get(i);
  2767                         if (checkExisting(child, lstChild) == false) {
  2768                             lstChild.add(child);
  2769                         }
  2770                     }
  2771                 }
  2772             } //If block
  2773             else if ((image.equalsIgnoreCase("CASE")) && isFound) {
  2774                 int beforeOff = tmp.offset(tokenHierarchy);
  2775                 List children = checkCaseBlock(tmp, ts, lstChild, false);
  2776                 if (children == null || children.isEmpty()) {//If inner check seems to have failed need to continue this one
  2777 
  2778                     ts.move(beforeOff);
  2779                     moveNext = ts.moveNext();
  2780                 } else {
  2781                     for (int i = 0; i
  2782                             < children.size(); i++) {
  2783                         PlsqlBlock child = (PlsqlBlock) children.get(i);
  2784                         if (checkExisting(child, lstChild) == false) {
  2785                             lstChild.add(child);
  2786                         }
  2787                     }
  2788                 }
  2789             } else if ((tokenID == PlsqlTokenId.KEYWORD)
  2790                     && ((image.equalsIgnoreCase("LOOP"))
  2791                     || (image.equalsIgnoreCase("WHILE"))
  2792                     || (image.equalsIgnoreCase("FOR")))) {
  2793                 if (isFound) {
  2794                     int beforeOff = tmp.offset(tokenHierarchy);
  2795                     if (!unsuccessBlocks.contains(beforeOff)) {
  2796                         PlsqlBlock child = checkLoopBlock(tmp, ts, lstChild);
  2797                         if (child == null) {//If inner check seems to have failed need to continue this one
  2798                             unsuccessBlocks.add(beforeOff);
  2799                             ts.move(beforeOff);
  2800                             moveNext = ts.moveNext();
  2801                         } else {
  2802                             if (checkExisting(child, lstChild) == false) {
  2803                                 lstChild.add(child);
  2804                             }
  2805                         }
  2806                     }
  2807                 }
  2808             } else if ((tokenID == PlsqlTokenId.KEYWORD) && (image.equalsIgnoreCase("TABLE")
  2809                     || image.equalsIgnoreCase("INDEX")
  2810                     || image.equalsIgnoreCase("SELECT")
  2811                     || image.equalsIgnoreCase("UPDATE")
  2812                     || image.equalsIgnoreCase("DELETE")
  2813                     || image.equalsIgnoreCase("INSERT")
  2814                     || image.equalsIgnoreCase("MERGE")
  2815                     || image.equalsIgnoreCase("DROP")
  2816                     || image.equalsIgnoreCase("SEQUENCE"))) {
  2817                 if (!isNotBlockStart(tmp, ts)) {
  2818                     int offset = tmp.offset(tokenHierarchy);
  2819                     PlsqlBlock child = checkStatementBlock(tmp, ts, parentBlocks);
  2820                     if (child == null) {//If inner check seems to have failed need to continue this one
  2821 
  2822                         ts.move(offset);
  2823                         ts.moveNext();
  2824                     } else {
  2825                         if (checkExisting(child, lstChild) == false) {
  2826                             lstChild.add(child);
  2827                         }
  2828                     }
  2829                 }
  2830             } else if (tokenID == PlsqlTokenId.LINE_COMMENT) {
  2831                 //only single comment line
  2832                 if (image.toUpperCase(Locale.ENGLISH).contains("<FOLD>")) {
  2833                     customStartToken = tmp;
  2834                 } else if (image.toUpperCase(Locale.ENGLISH).contains("<END-FOLD>")) {
  2835                     if (customStartToken != null) {
  2836                         String name = customStartToken.text().toString();
  2837                         int index = name.toUpperCase(Locale.ENGLISH).indexOf("<FOLD>");
  2838                         name =
  2839                                 name.substring(index + 7).trim();
  2840                         if (ts.moveNext()) {
  2841                             tmp = ts.token();
  2842                             PlsqlBlock custom = new PlsqlBlock(customStartToken.offset(tokenHierarchy),
  2843                                     tmp.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
  2844                             customFoldBlocks.add(custom);
  2845                         }
  2846                         customStartToken = null;
  2847                     }
  2848                 } else {
  2849                     PlsqlBlock child = checkComment(tmp, ts);
  2850                     if ((child != null) && (checkExisting(child, lstChild) == false)) {
  2851                         lstChild.add(child);
  2852                     }
  2853                 }
  2854             } else if (tokenID == PlsqlTokenId.BLOCK_COMMENT) {
  2855                 int start = tmp.offset(tokenHierarchy);
  2856                 PlsqlBlock child = new PlsqlBlock(start,
  2857                         start + tmp.length(), "BLOCK COMMENT", "", PlsqlBlockType.COMMENT);
  2858                 if (checkExisting(child, lstChild) == false) {
  2859                     lstChild.add(child);
  2860                 }
  2861             } else if (tokenID == PlsqlTokenId.KEYWORD && (image.equalsIgnoreCase("BEGIN"))) {
  2862                 beginCount++;
  2863             } else if (tokenID == PlsqlTokenId.KEYWORD && image.equalsIgnoreCase("END")) {
  2864                 int off = ts.offset();
  2865                 if (getNextNonWhitespace(ts, true)) {
  2866                     tmp = ts.token();
  2867                 }
  2868 
  2869                 if ((tmp.text().toString().equals(";")) || (tmp.id() == PlsqlTokenId.IDENTIFIER)
  2870                         || (tmp.id() == PlsqlTokenId.KEYWORD && (!tmp.toString().equalsIgnoreCase("IF")
  2871                         && !tmp.toString().equalsIgnoreCase("CASE") && !tmp.toString().equalsIgnoreCase("LOOP")))) {
  2872                     beginCount--;
  2873                 }
  2874 
  2875                 ts.move(off);
  2876                 ts.moveNext();
  2877                 tmp = ts.token();
  2878             } else if (tokenID == PlsqlTokenId.KEYWORD && image.equalsIgnoreCase("PRAGMA")) {
  2879                 pragmaFound = true;
  2880             } else {
  2881                 //Mark when we come to 'IS'
  2882                 if ((tokenID == PlsqlTokenId.KEYWORD)
  2883                         && ((image.equalsIgnoreCase("IS")))) {
  2884                     isFound = true;
  2885                 }
  2886             }
  2887             if (tokenID != PlsqlTokenId.WHITESPACE) {
  2888                 previous = tmp;
  2889             }
  2890 
  2891             moveNext = ts.moveNext();
  2892             tmp = ts.token();
  2893         }
  2894 
  2895         if (block != null) {
  2896             //check whether there is a parent block
  2897             PlsqlBlock parent = getParentBlock(blockHierarchy, block.getStartOffset(), block.getEndOffset());
  2898             if ((parent != null) && (parent.getName().equals(block.getName()))) {
  2899                 if (parent.getEndOffset() == block.getEndOffset()) {
  2900                     return null;
  2901                 }
  2902             }
  2903 
  2904             //add children
  2905             addChildren(block, lstChild, parentBlocks);
  2906         }
  2907 
  2908         return block;
  2909     }
  2910 
  2911     /**
  2912      * Check whether this is the start of a PACKAGE block
  2913      *
  2914      * @param tempToken
  2915      * @param ts
  2916      * @return
  2917      */
  2918     private PlsqlBlock checkPackage(Token<PlsqlTokenId> packToken, TokenSequence<PlsqlTokenId> ts, List<PlsqlBlock> parentBlocks) {
  2919         Token<PlsqlTokenId> packBegin = packToken;
  2920         Token<PlsqlTokenId> tmp = packToken;
  2921         Token<PlsqlTokenId> tmpPre = packToken;
  2922         boolean isFound = false;
  2923         String packageName = "";
  2924         boolean isPackageBody = false;
  2925         boolean moveNext = false;
  2926         PlsqlBlock block = null;
  2927         List<PlsqlBlock> lstChild = new ArrayList<PlsqlBlock>();
  2928         int beginCount = 0;
  2929 
  2930         //Check whether the beginning is in a SQL Plus command
  2931         if (sqlPlusLine(ts)) {
  2932             return null;
  2933         }
  2934 
  2935         //Get package name which is the next non whitespace token in spec
  2936         moveNext = getNextNonWhitespace(ts, true);
  2937         tmp = ts.token();
  2938         if (moveNext == false) {
  2939             return block;
  2940         }
  2941 
  2942         if (tmp.text().toString().equalsIgnoreCase("BODY")) {
  2943             isPackageBody = true;
  2944             moveNext = getNextNonWhitespace(ts, true);
  2945             tmp = ts.token();
  2946             if (moveNext == false) {
  2947                 return block;
  2948             }
  2949         }
  2950 
  2951         packageName = tmp.text().toString();
  2952         packageName = checkForOtherSchema(ts, packageName);
  2953         String alias = "";
  2954         if (packageName.indexOf('&') != -1) {
  2955             alias = packageName;
  2956         } else if (hasDefineKey(packageName)) {
  2957             alias = '&' + getDefineKey(packageName);
  2958         }
  2959 
  2960         packageName = getDefine(packageName);
  2961         Token<PlsqlTokenId> customStartToken = null;
  2962 
  2963         while (moveNext) {
  2964             String image = tmp.text().toString();
  2965             PlsqlTokenId tokenID = tmp.id();
  2966 
  2967             if ((!image.equals(";")) && (tmp.offset(tokenHierarchy) > endParse)) {
  2968                 break;
  2969             }
  2970 
  2971             //We might have come to the end of the package
  2972             if (((tokenID == PlsqlTokenId.OPERATOR) && (image.equals(";") || (image.equals("/") && checkForOnlyChar(ts, ts.offset())))) && (isFound)
  2973                     && ((tmpPre.text().toString().equalsIgnoreCase(packageName))
  2974                     || ((!alias.equals("")) && (tmpPre.text().toString().equalsIgnoreCase(alias)))
  2975                     || ((tmpPre.text().toString().equalsIgnoreCase("END")) && (beginCount < 0)))) {
  2976                 boolean isPackage = false;
  2977                 if (tmpPre.text().toString().equalsIgnoreCase(packageName)
  2978                         || tmpPre.text().toString().equalsIgnoreCase(alias)) {
  2979                     //check whether previous Non white space token to the identifier is END
  2980                     int offset = ts.offset();
  2981                     boolean preMove = getPreviousNonWhitespace(ts, true);
  2982                     preMove = getPreviousNonWhitespace(ts, true);
  2983                     Token<PlsqlTokenId> previousNWS = ts.token();
  2984 
  2985                     ts.move(offset);
  2986                     ts.moveNext();
  2987 
  2988                     if ((preMove != false)
  2989                             && previousNWS.text().toString().equalsIgnoreCase("END")) {
  2990                         isPackage = true;
  2991                     }
  2992                 } else if ((tmpPre.text().toString().equalsIgnoreCase("END")) && (beginCount < 0)) {
  2993                     isPackage = true;
  2994                 }
  2995 
  2996                 //If this is a package end create the block
  2997                 if (isPackage) {
  2998                     PlsqlBlockType type = PlsqlBlockType.PACKAGE;
  2999 
  3000                     if (isPackageBody) {
  3001                         type = PlsqlBlockType.PACKAGE_BODY;
  3002                     }
  3003 
  3004                     ts.moveNext();
  3005 
  3006                     block = new PlsqlBlock(packBegin.offset(tokenHierarchy),
  3007                             ts.offset(), packageName, alias, type);
  3008                     checkPrefix(packBegin.offset(tokenHierarchy), ts, block);
  3009                     break;
  3010                 }
  3011             } else if ((tokenID == PlsqlTokenId.KEYWORD)
  3012                     && ((image.equalsIgnoreCase("PROCEDURE"))
  3013                     || (image.equalsIgnoreCase("FUNCTION"))
  3014                     || (image.equalsIgnoreCase("CURSOR")))) {
  3015                 if (isFound) {
  3016                     int beforeOff = tmp.offset(tokenHierarchy);
  3017 
  3018                     if (image.equalsIgnoreCase("PROCEDURE")) {
  3019                         PlsqlBlock child = checkMethod(tmp, ts, PlsqlBlockType.PROCEDURE_IMPL, lstChild);
  3020                         if (child == null) {//If inner check seems to have failed need to continue this one
  3021 
  3022                             ts.move(beforeOff);
  3023                             ts.moveNext();
  3024                         } else {
  3025                             if (checkExisting(child, lstChild) == false) {
  3026                                 lstChild.add(child);
  3027                             }
  3028                         }
  3029                     } //Inner procedure
  3030                     else if (image.equalsIgnoreCase("FUNCTION")) {
  3031                         PlsqlBlock child = checkMethod(tmp, ts, PlsqlBlockType.FUNCTION_IMPL, lstChild);
  3032                         if (child == null) {//If inner check seems to have failed need to continue this one
  3033 
  3034                             ts.move(beforeOff);
  3035                             ts.moveNext();
  3036                         } else {
  3037                             if (checkExisting(child, lstChild) == false) {
  3038                                 lstChild.add(child);
  3039                             }
  3040                         }
  3041                     } //Inner function
  3042                     else if (image.equalsIgnoreCase("CURSOR")) {
  3043                         PlsqlBlock child = checkCursor(tmp, ts, lstChild);
  3044                         if (child == null) {//If inner check seems to have failed need to continue this one
  3045 
  3046                             ts.move(beforeOff);
  3047                             ts.moveNext();
  3048                         } else {
  3049                             if (checkExisting(child, lstChild) == false) {
  3050                                 lstChild.add(child);
  3051                             }
  3052                         }
  3053                     } //Inner cursor
  3054 
  3055                 } else {
  3056                     break;
  3057                 }
  3058             } else if (tokenID == PlsqlTokenId.KEYWORD && (image.equalsIgnoreCase("BEGIN"))) {
  3059                 beginCount++;
  3060             } else if ((tokenID == PlsqlTokenId.KEYWORD)
  3061                     && (image.equalsIgnoreCase("END"))) {
  3062                 int off = ts.offset();
  3063                 if (getNextNonWhitespace(ts, true)) {
  3064                     tmp = ts.token();
  3065                 }
  3066 
  3067                 if ((tmp.text().toString().equals(";")) || (tmp.id() == PlsqlTokenId.IDENTIFIER)
  3068                         || (tmp.id() == PlsqlTokenId.KEYWORD && (!tmp.toString().equalsIgnoreCase("IF")
  3069                         && !tmp.toString().equalsIgnoreCase("CASE") && !tmp.toString().equalsIgnoreCase("LOOP")))) {
  3070                     beginCount--;
  3071                 }
  3072 
  3073                 ts.move(off);
  3074                 ts.moveNext();
  3075                 tmp = ts.token();
  3076             } else if ((tokenID == PlsqlTokenId.KEYWORD)
  3077                     && ((image.equalsIgnoreCase("CREATE")) || (image.equalsIgnoreCase("PACKAGE")))) {
  3078                 return block;
  3079             } else if (tokenID == PlsqlTokenId.LINE_COMMENT) {
  3080                 //only single comment line
  3081                 if (image.toUpperCase(Locale.ENGLISH).contains("<FOLD>")) {
  3082                     customStartToken = tmp;
  3083                 } else if (image.toUpperCase(Locale.ENGLISH).contains("<END-FOLD>")) {
  3084                     if (customStartToken != null) {
  3085                         String name = customStartToken.text().toString();
  3086                         int index = name.toUpperCase(Locale.ENGLISH).indexOf("<FOLD>");
  3087                         name = name.substring(index + 7).trim();
  3088                         if (ts.moveNext()) {
  3089                             tmp = ts.token();
  3090                             PlsqlBlock custom = new PlsqlBlock(customStartToken.offset(tokenHierarchy),
  3091                                     tmp.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
  3092                             customFoldBlocks.add(custom);
  3093                         }
  3094                         customStartToken = null;
  3095                     }
  3096                 } else {
  3097                     PlsqlBlock child = checkComment(tmp, ts);
  3098                     if ((child != null) && (checkExisting(child, lstChild) == false)) {
  3099                         lstChild.add(child);
  3100                     }
  3101                 }
  3102             } else if (tokenID == PlsqlTokenId.BLOCK_COMMENT) {
  3103                 int start = tmp.offset(tokenHierarchy);
  3104                 PlsqlBlock child = new PlsqlBlock(start,
  3105                         start + tmp.length(), "BLOCK COMMENT", "", PlsqlBlockType.COMMENT);
  3106                 if (checkExisting(child, lstChild) == false) {
  3107                     lstChild.add(child);
  3108                 }
  3109             } else {
  3110                 //Mark when we come to 'IS'
  3111                 if ((tokenID == PlsqlTokenId.KEYWORD)
  3112                         && ((image.equalsIgnoreCase("IS")) || (image.equalsIgnoreCase("AS")))) {
  3113                     isFound = true;
  3114                 }
  3115             }
  3116 
  3117             if (tokenID != PlsqlTokenId.WHITESPACE) {//previous non whitespace token
  3118 
  3119                 tmpPre = tmp;
  3120             }
  3121 
  3122             moveNext = ts.moveNext();
  3123             tmp = ts.token();
  3124         }
  3125 
  3126         if (block != null) {
  3127             //add children
  3128             addChildren(block, lstChild, parentBlocks);
  3129 
  3130             //Add immediate children
  3131             addImmediateChildren(block, parentBlocks);
  3132         }
  3133 
  3134         return block;
  3135     }
  3136 
  3137     /**
  3138      * Method that will check declare end blocks
  3139      *
  3140      * @param current
  3141      * @param ts
  3142      * @param parentBlocks
  3143      * @returns
  3144      */
  3145     private PlsqlBlock checkDeclareBlock(Token<PlsqlTokenId> current, TokenSequence<PlsqlTokenId> ts, List<PlsqlBlock> parentBlocks) {
  3146         Token<PlsqlTokenId> declareBegin;
  3147         Token<PlsqlTokenId> token = null;
  3148         boolean moveNext = false;
  3149         boolean isBeginFound = false;
  3150         List<PlsqlBlock> lstChild = new ArrayList<PlsqlBlock>();
  3151         PlsqlBlock block = null;
  3152 
  3153         //Check whether the beginning is in a SQL Plus command
  3154         if (sqlPlusLine(ts)) {
  3155             return null;
  3156         }
  3157 
  3158         moveNext = ts.moveNext();
  3159         token = ts.token();
  3160         declareBegin = current;
  3161         Token<PlsqlTokenId> customStartToken = null;
  3162 
  3163         while (moveNext) {
  3164             PlsqlTokenId tokenID = token.id();
  3165             String image = token.text().toString();
  3166 
  3167             if ((token != null) && (!image.equals(";")) && (token.offset(tokenHierarchy) > endParse)) {
  3168                 break;
  3169             }
  3170 
  3171             //We have come to the end of the comment
  3172             if ((tokenID == PlsqlTokenId.OPERATOR) && ((image.equals("/") && checkForOnlyChar(ts, ts.offset())) || image.equals(";"))) {
  3173                 //check whether its END; or END before /
  3174                 boolean movePre = getPreviousNonWhitespace(ts, true);
  3175                 Token<PlsqlTokenId> pre = ts.token();
  3176                 if (!movePre) {
  3177                     ts.move(token.offset(tokenHierarchy));
  3178                     ts.moveNext();
  3179                 }
  3180 
  3181                 if (image.equals("/")) {
  3182                     if (pre.toString().equals(";")) {
  3183                         movePre = getPreviousNonWhitespace(ts, true);
  3184                         pre = ts.token();
  3185                         if ((movePre) && (pre.toString().equalsIgnoreCase("END"))) {
  3186                             ts.move(token.offset(tokenHierarchy));
  3187                             ts.moveNext();
  3188                             ts.moveNext();
  3189                             block = new PlsqlBlock(declareBegin.offset(tokenHierarchy),
  3190                                     ts.offset(), "", "", PlsqlBlockType.DECLARE_END);
  3191                             removeChildBegin(block);
  3192                             break;
  3193                         }
  3194                     } else {
  3195                         //something has gone wrong '/' is a terminal
  3196                         break;
  3197                     }
  3198                 } else {
  3199                     if ((movePre) && (pre.toString().equalsIgnoreCase("END"))) {
  3200                         ts.move(token.offset(tokenHierarchy));
  3201                         ts.moveNext();
  3202                         ts.moveNext();
  3203                         block = new PlsqlBlock(declareBegin.offset(tokenHierarchy),
  3204                                 ts.offset(), "", "", PlsqlBlockType.DECLARE_END);
  3205                         removeChildBegin(block);
  3206                         break;
  3207                     }
  3208                 }
  3209                 ts.move(token.offset(tokenHierarchy));
  3210                 ts.moveNext();
  3211             } else if (image.equalsIgnoreCase("CURSOR")) {
  3212                 int beforeOff = token.offset(tokenHierarchy);
  3213                 PlsqlBlock child = checkCursor(token, ts, lstChild);
  3214                 if (child == null) {//If inner check seems to have failed need to continue this one
  3215 
  3216                     ts.move(beforeOff);
  3217                     moveNext = ts.moveNext();
  3218                 } else {
  3219                     if (checkExisting(child, lstChild) == false) {
  3220                         lstChild.add(child);
  3221                     }
  3222                 }
  3223             } else if (image.equalsIgnoreCase("PROCEDURE")) {
  3224                 if (isBeginFound) {//Can be there only before begin in a declare block
  3225 
  3226                     break;
  3227                 }
  3228 
  3229                 int beforeOff = token.offset(tokenHierarchy);
  3230                 PlsqlBlock child = checkMethod(token, ts, PlsqlBlockType.PROCEDURE_IMPL, lstChild);
  3231                 if (child == null) {//If inner check seems to have failed need to continue this one
  3232 
  3233                     ts.move(beforeOff);
  3234                     moveNext = ts.moveNext();
  3235                 } else {
  3236                     if (checkExisting(child, lstChild) == false) {
  3237                         lstChild.add(child);
  3238                     }
  3239                 }
  3240             } else if (image.equalsIgnoreCase("FUNCTION")) {
  3241                 if (isBeginFound) {//Can be there only before begin in a declare block
  3242 
  3243                     break;
  3244                 }
  3245 
  3246                 int beforeOff = token.offset(tokenHierarchy);
  3247                 PlsqlBlock child = checkMethod(token, ts, PlsqlBlockType.FUNCTION_IMPL, lstChild);
  3248                 if (child == null) {//If inner check seems to have failed need to continue this one
  3249 
  3250                     ts.move(beforeOff);
  3251                     moveNext = ts.moveNext();
  3252                 } else {
  3253                     if (checkExisting(child, lstChild) == false) {
  3254                         lstChild.add(child);
  3255                     }
  3256                 }
  3257             } else if (isBeginFound && image.equalsIgnoreCase("DECLARE")) {
  3258                 int beforeOff = token.offset(tokenHierarchy);
  3259                 PlsqlBlock child = checkDeclareBlock(token, ts, lstChild);
  3260                 if (child != null) {//If inner check seems to have failed need to continue this one
  3261                     if (checkExisting(child, lstChild) == false) {
  3262                         lstChild.add(child);
  3263                     }
  3264                 }
  3265             } else if (image.equalsIgnoreCase("BEGIN")) {
  3266                 if (isBeginFound) {
  3267                     int beforeOff = token.offset(tokenHierarchy);
  3268                     PlsqlBlock child = checkBeginBlock(token, ts, lstChild);
  3269                     if (child == null) {//If inner check seems to have failed need to continue this one
  3270 
  3271                         ts.move(beforeOff);
  3272                         moveNext = ts.moveNext();
  3273                     } else {
  3274                         if (checkExisting(child, lstChild) == false) {
  3275                             lstChild.add(child);
  3276                         }
  3277                     }
  3278                 } else {
  3279                     isBeginFound = true;
  3280                 }
  3281             } else if (image.equalsIgnoreCase("IF")) {
  3282                 int beforeOff = token.offset(tokenHierarchy);
  3283                 List children = checkIfBlock(token, ts, lstChild);
  3284                 if (children == null || children.isEmpty()) {//If inner check seems to have failed need to continue this one
  3285 
  3286                     ts.move(beforeOff);
  3287                     moveNext = ts.moveNext();
  3288                 } else {
  3289                     for (int i = 0; i < children.size(); i++) {
  3290                         PlsqlBlock child = (PlsqlBlock) children.get(i);
  3291                         if (checkExisting(child, lstChild) == false) {
  3292                             lstChild.add(child);
  3293                         }
  3294                     }
  3295                 }
  3296             } else if (image.equalsIgnoreCase("CASE")) {
  3297                 int beforeOff = token.offset(tokenHierarchy);
  3298                 List children = checkCaseBlock(token, ts, lstChild, false);
  3299                 if (children == null || children.isEmpty()) {//If inner check seems to have failed need to continue this one
  3300 
  3301                     ts.move(beforeOff);
  3302                     moveNext = ts.moveNext();
  3303                 } else {
  3304                     for (int i = 0; i < children.size(); i++) {
  3305                         PlsqlBlock child = (PlsqlBlock) children.get(i);
  3306                         if (checkExisting(child, lstChild) == false) {
  3307                             lstChild.add(child);
  3308                         }
  3309                     }
  3310                 }
  3311             } else if (image.equalsIgnoreCase("LOOP")
  3312                     || image.equalsIgnoreCase("WHILE")
  3313                     || image.equalsIgnoreCase("FOR")) {
  3314                 int beforeOff = token.offset(tokenHierarchy);
  3315                 if (!unsuccessBlocks.contains(beforeOff)) {
  3316                     PlsqlBlock child = checkLoopBlock(token, ts, lstChild);
  3317                     if (child == null) {//If inner check seems to have failed need to continue this one
  3318                         unsuccessBlocks.add(beforeOff);
  3319                         ts.move(beforeOff);
  3320                         moveNext = ts.moveNext();
  3321                     } else {
  3322                         if (checkExisting(child, lstChild) == false) {
  3323                             lstChild.add(child);
  3324                         }
  3325                     }
  3326                 }
  3327             } else if (image.equalsIgnoreCase("TABLE")
  3328                     || image.equalsIgnoreCase("INDEX")
  3329                     || image.equalsIgnoreCase("SELECT")
  3330                     || image.equalsIgnoreCase("UPDATE")
  3331                     || image.equalsIgnoreCase("DELETE")
  3332                     || image.equalsIgnoreCase("INSERT")
  3333                     || image.equalsIgnoreCase("MERGE")
  3334                     || image.equalsIgnoreCase("DROP")
  3335                     || image.equalsIgnoreCase("SEQUENCE")) {
  3336                 if (!isNotBlockStart(token, ts)) {
  3337                     int offset = token.offset(tokenHierarchy);
  3338                     PlsqlBlock child = checkStatementBlock(token, ts, parentBlocks);
  3339                     if (child == null) {//If inner check seems to have failed need to continue this one
  3340 
  3341                         ts.move(offset);
  3342                         ts.moveNext();
  3343                     } else {
  3344                         if (checkExisting(child, lstChild) == false) {
  3345                             lstChild.add(child);
  3346                         }
  3347                     }
  3348                 }
  3349             } else if (tokenID == PlsqlTokenId.LINE_COMMENT) {
  3350                 //only single comment line
  3351                 if (image.toUpperCase(Locale.ENGLISH).contains("<FOLD>")) {
  3352                     customStartToken = token;
  3353                 } else if (image.toUpperCase(Locale.ENGLISH).contains("<END-FOLD>")) {
  3354                     if (customStartToken != null) {
  3355                         String name = customStartToken.text().toString();
  3356                         int index = name.toUpperCase(Locale.ENGLISH).indexOf("<FOLD>");
  3357                         name = name.substring(index + 7).trim();
  3358                         if (ts.moveNext()) {
  3359                             token = ts.token();
  3360                             PlsqlBlock custom = new PlsqlBlock(customStartToken.offset(tokenHierarchy),
  3361                                     token.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
  3362                             customFoldBlocks.add(custom);
  3363                         }
  3364 
  3365                         customStartToken = null;
  3366                     }
  3367                 } else {
  3368                     PlsqlBlock child = checkComment(token, ts);
  3369                     if (child != null) {
  3370                         if (checkExisting(child, lstChild) == false) {
  3371                             lstChild.add(child);
  3372                         }
  3373                     }
  3374                 }
  3375             } else if (tokenID == PlsqlTokenId.BLOCK_COMMENT) {
  3376                 int start = token.offset(tokenHierarchy);
  3377                 PlsqlBlock child = new PlsqlBlock(start,
  3378                         start + token.length(), "BLOCK COMMENT", "", PlsqlBlockType.COMMENT);
  3379                 if (child != null) {
  3380                     if (checkExisting(child, lstChild) == false) {
  3381                         lstChild.add(child);
  3382                     }
  3383                 }
  3384             }
  3385 
  3386             moveNext = ts.moveNext();
  3387             token = ts.token();
  3388         }
  3389 
  3390         if (block != null) {
  3391             //add children
  3392             addChildren(block, lstChild, parentBlocks);
  3393         } else {
  3394             if (moveNext) { //If have come to last return otherwise we will loop with begin
  3395                 ts.move(declareBegin.offset(tokenHierarchy));
  3396                 ts.moveNext();
  3397             }
  3398         }
  3399 
  3400         return block;
  3401     }
  3402 
  3403     /**
  3404      * Method that will check begin end blocks
  3405      *
  3406      * @param current
  3407      * @param ts
  3408      * @param parentBlocks
  3409      * @returns
  3410      */
  3411     private PlsqlBlock checkBeginBlock(Token<PlsqlTokenId> current, TokenSequence<PlsqlTokenId> ts, List<PlsqlBlock> parentBlocks) {
  3412         Token<PlsqlTokenId> begin;
  3413         Token<PlsqlTokenId> token = null;
  3414         PlsqlBlock block = null;
  3415         List<PlsqlBlock> lstChild = new ArrayList<PlsqlBlock>();
  3416         boolean moveNext = false;
  3417 
  3418         //Check whether the beginning is in a SQL Plus command
  3419         if (sqlPlusLine(ts)) {
  3420             return null;
  3421         }
  3422 
  3423         moveNext = ts.moveNext();
  3424         token = ts.token();
  3425         begin = current;
  3426         Token<PlsqlTokenId> customStartToken = null;
  3427 
  3428         while (moveNext) {
  3429             PlsqlTokenId tokenID = token.id();
  3430             String image = token.text().toString();
  3431 
  3432             if ((token != null) && (!image.equals(";")) && (token.offset(tokenHierarchy) > endParse)) {
  3433                 break;
  3434             }
  3435 
  3436             //We have come to the end of the comment
  3437             if ((tokenID == PlsqlTokenId.OPERATOR) && ((image.equals("/") && checkForOnlyChar(ts, ts.offset())) || image.equals(";"))) {
  3438                 //check whether its END; or END; before /
  3439                 boolean movePre = getPreviousNonWhitespace(ts, true);
  3440                 Token<PlsqlTokenId> pre = ts.token();
  3441                 if (!movePre) {
  3442                     ts.move(token.offset(tokenHierarchy));
  3443                     ts.moveNext();
  3444                 }
  3445 
  3446                 if (image.equals("/")) {
  3447                     if (pre.toString().equals(";")) {
  3448                         movePre = getPreviousNonWhitespace(ts, true);
  3449                         pre = ts.token();
  3450                         if ((movePre) && (pre.toString().equalsIgnoreCase("END"))) {
  3451                             //check whether there is a  DECLARE_END parent block with the same offset
  3452                             ts.move(token.offset(tokenHierarchy));
  3453                             ts.moveNext();
  3454                             ts.moveNext();
  3455                             int start = begin.offset(tokenHierarchy);
  3456                             int end = ts.offset();
  3457                             PlsqlBlock parent = getParentBlock(blockHierarchy, start, end);
  3458                             if ((parent == null) || (parent.getEndOffset() != end)) {
  3459                                 block = new PlsqlBlock(start, end, "", "", PlsqlBlockType.BEGIN_END);
  3460                             }
  3461 
  3462                             break;
  3463 
  3464                         }
  3465                     } else {
  3466                         //something has gone wrong '/' is a terminal
  3467                         break;
  3468                     }
  3469                 } else {
  3470                     if ((movePre) && (pre.toString().equalsIgnoreCase("END"))) {
  3471                         //check whether there is a  DECLARE_END parent block with the same offset
  3472                         ts.move(token.offset(tokenHierarchy));
  3473                         ts.moveNext();
  3474                         ts.moveNext();
  3475                         int start = begin.offset(tokenHierarchy);
  3476                         int end = ts.offset();
  3477                         PlsqlBlock parent = getParentBlock(blockHierarchy, start, end);
  3478                         if ((parent == null) || (parent.getEndOffset() != end)) {
  3479                             block = new PlsqlBlock(start, end, "", "", PlsqlBlockType.BEGIN_END);
  3480                         }
  3481 
  3482                         break;
  3483                     }
  3484                 }
  3485                 ts.move(token.offset(tokenHierarchy));
  3486                 ts.moveNext();
  3487             } else if (tokenID == PlsqlTokenId.KEYWORD && image.equalsIgnoreCase("DECLARE")) {
  3488                 int beforeOff = token.offset(tokenHierarchy);
  3489                 PlsqlBlock child = checkDeclareBlock(token, ts, lstChild);
  3490                 if (child != null) {//If inner check seems to have failed need to continue this one
  3491                     if (checkExisting(child, lstChild) == false) {
  3492                         lstChild.add(child);
  3493                     }
  3494                 }
  3495             } else if (tokenID == PlsqlTokenId.KEYWORD && (image.equalsIgnoreCase("BEGIN"))) {
  3496                 int beforeOff = token.offset(tokenHierarchy);
  3497                 PlsqlBlock child = checkBeginBlock(token, ts, lstChild);
  3498                 if (child == null) {//If inner check seems to have failed need to continue this one
  3499 
  3500                     ts.move(beforeOff);
  3501                     moveNext = ts.moveNext();
  3502                 } else {
  3503                     if (checkExisting(child, lstChild) == false) {
  3504                         lstChild.add(child);
  3505                     }
  3506                 }
  3507             } else if (image.equalsIgnoreCase("IF")) {
  3508                 int beforeOff = token.offset(tokenHierarchy);
  3509                 List children = checkIfBlock(token, ts, lstChild);
  3510                 if (children == null || children.isEmpty()) {//If inner check seems to have failed need to continue this one
  3511 
  3512                     ts.move(beforeOff);
  3513                     moveNext = ts.moveNext();
  3514                 } else {
  3515                     for (int i = 0; i < children.size(); i++) {
  3516                         PlsqlBlock child = (PlsqlBlock) children.get(i);
  3517                         if (checkExisting(child, lstChild) == false) {
  3518                             lstChild.add(child);
  3519                         }
  3520                     }
  3521                 }
  3522             } else if (image.equalsIgnoreCase("CASE")) {
  3523                 int beforeOff = token.offset(tokenHierarchy);
  3524                 List children = checkCaseBlock(token, ts, lstChild, false);
  3525                 if (children == null || children.isEmpty()) {//If inner check seems to have failed need to continue this one
  3526 
  3527                     ts.move(beforeOff);
  3528                     moveNext = ts.moveNext();
  3529                 } else {
  3530                     for (int i = 0; i < children.size(); i++) {
  3531                         PlsqlBlock child = (PlsqlBlock) children.get(i);
  3532                         if (checkExisting(child, lstChild) == false) {
  3533                             lstChild.add(child);
  3534                         }
  3535                     }
  3536                 }
  3537             } else if (image.equalsIgnoreCase("LOOP")
  3538                     || image.equalsIgnoreCase("WHILE")
  3539                     || image.equalsIgnoreCase("FOR")) {
  3540                 int beforeOff = token.offset(tokenHierarchy);
  3541                 if (!unsuccessBlocks.contains(beforeOff)) {
  3542                     PlsqlBlock child = checkLoopBlock(token, ts, lstChild);
  3543                     if (child == null) {//If inner check seems to have failed need to continue this one
  3544                         unsuccessBlocks.add(beforeOff);
  3545                         ts.move(beforeOff);
  3546                         moveNext = ts.moveNext();
  3547                     } else {
  3548                         if (checkExisting(child, lstChild) == false) {
  3549                             lstChild.add(child);
  3550                         }
  3551                     }
  3552                 }
  3553             } else if (image.equalsIgnoreCase("TABLE")
  3554                     || image.equalsIgnoreCase("INDEX")
  3555                     || image.equalsIgnoreCase("SELECT")
  3556                     || image.equalsIgnoreCase("UPDATE")
  3557                     || image.equalsIgnoreCase("DELETE")
  3558                     || image.equalsIgnoreCase("INSERT")
  3559                     || image.equalsIgnoreCase("MERGE")
  3560                     || image.equalsIgnoreCase("DROP")
  3561                     || image.equalsIgnoreCase("SEQUENCE")) {
  3562                 if (!isNotBlockStart(token, ts)) {
  3563                     int offset = token.offset(tokenHierarchy);
  3564                     PlsqlBlock child = checkStatementBlock(token, ts, parentBlocks);
  3565                     if (child == null) {//If inner check seems to have failed need to continue this one
  3566 
  3567                         ts.move(offset);
  3568                         ts.moveNext();
  3569                     } else {
  3570                         if (checkExisting(child, lstChild) == false) {
  3571                             lstChild.add(child);
  3572                         }
  3573                     }
  3574                 }
  3575             } else if (image.equalsIgnoreCase("PROCEDURE")
  3576                     || image.equalsIgnoreCase("FUNCTION")) {
  3577                 break;
  3578             } else if (tokenID == PlsqlTokenId.LINE_COMMENT) {
  3579                 //only single comment line
  3580                 if (image.toUpperCase(Locale.ENGLISH).contains("<FOLD>")) {
  3581                     customStartToken = token;
  3582                 } else if (image.toUpperCase(Locale.ENGLISH).contains("<END-FOLD>")) {
  3583                     if (customStartToken != null) {
  3584                         String name = customStartToken.text().toString();
  3585                         int index = name.toUpperCase(Locale.ENGLISH).indexOf("<FOLD>");
  3586                         name = name.substring(index + 7).trim();
  3587                         if (ts.moveNext()) {
  3588                             token = ts.token();
  3589                             PlsqlBlock custom = new PlsqlBlock(customStartToken.offset(tokenHierarchy),
  3590                                     token.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
  3591                             customFoldBlocks.add(custom);
  3592                         }
  3593 
  3594                         customStartToken = null;
  3595                     }
  3596                 } else {
  3597                     PlsqlBlock child = checkComment(token, ts);
  3598                     if (child != null) {
  3599                         if (checkExisting(child, lstChild) == false) {
  3600                             lstChild.add(child);
  3601                         }
  3602                     }
  3603                 }
  3604             } else if (tokenID == PlsqlTokenId.BLOCK_COMMENT) {
  3605                 int start = token.offset(tokenHierarchy);
  3606                 PlsqlBlock child = new PlsqlBlock(start,
  3607                         start + token.length(), "BLOCK COMMENT", "", PlsqlBlockType.COMMENT);
  3608                 if (child != null) {
  3609                     if (checkExisting(child, lstChild) == false) {
  3610                         lstChild.add(child);
  3611                     }
  3612                 }
  3613             }
  3614 
  3615             moveNext = ts.moveNext();
  3616             token = ts.token();
  3617         }
  3618 
  3619         if (block != null) {
  3620             //add children
  3621             addChildren(block, lstChild, parentBlocks);
  3622         }
  3623 
  3624         return block;
  3625     }
  3626 
  3627     /**
  3628      * Method to check table & column comments
  3629      *
  3630      * @param current
  3631      * @param ts
  3632      * @param parentBlocks
  3633      * @returns
  3634      */
  3635     private PlsqlBlock checkTblColComment(Token<PlsqlTokenId> current, TokenSequence<PlsqlTokenId> ts, List<PlsqlBlock> parentBlocks) {
  3636         Token<PlsqlTokenId> commentBegin = current;
  3637         Token<PlsqlTokenId> tmpPre = current;
  3638         PlsqlBlock block = null;
  3639         List<PlsqlBlock> lstChild = new ArrayList<PlsqlBlock>();
  3640 
  3641         //Check whether the beginning is in a SQL Plus command
  3642         if (sqlPlusLine(ts)) {
  3643             return null;
  3644         }
  3645 
  3646         int beginOffset = ts.offset();
  3647         boolean isTable = false;
  3648         boolean isEnd = false;
  3649         Token<PlsqlTokenId> previousBlock = null;
  3650         String tableName = "";
  3651         boolean moveNext = false;
  3652         moveNext = getNextNonWhitespace(ts, true);
  3653         String alias = "";
  3654         Token<PlsqlTokenId> customStartToken = null;
  3655 
  3656         //Check whether that is table/column comment
  3657         if (moveNext == false) {
  3658             return block;
  3659         }
  3660 
  3661         Token<PlsqlTokenId> tmp = ts.token(); //catching ON
  3662 
  3663         moveNext = getNextNonWhitespace(ts, true);
  3664         tmp = ts.token();    //TABLE OR VIEW
  3665 
  3666         if ((moveNext != false) && (tmp.id() == PlsqlTokenId.KEYWORD)) {
  3667             String image = tmp.text().toString();
  3668             if (image.equalsIgnoreCase("TABLE")) {
  3669                 isTable = true;
  3670 
  3671                 moveNext = getNextNonWhitespace(ts, true);
  3672                 tmp = ts.token();
  3673 
  3674                 if (moveNext != false) {
  3675                     tableName = tmp.text().toString();
  3676                     tableName = checkForOtherSchema(ts, tableName);
  3677                     if (tableName.indexOf('&') != -1) {
  3678                         alias = tableName;
  3679                     }
  3680 
  3681                     tableName = getDefine(tableName);
  3682                     if (ts.moveNext()) {
  3683                         tmp = ts.token();
  3684                         if ((tmp.id() == PlsqlTokenId.DOT) && ts.moveNext()) {
  3685                             tmp = ts.token();
  3686                         }
  3687                     }
  3688                 }
  3689 
  3690             } else if (image.equalsIgnoreCase("COLUMN")) {
  3691                 isTable = false;
  3692                 moveNext = getNextNonWhitespace(ts, true);
  3693                 tmp = ts.token();
  3694 
  3695                 if (moveNext != false) {
  3696                     tableName = tmp.text().toString();
  3697                     if (tableName.indexOf('&') != -1) {
  3698                         alias = tableName;
  3699                     }
  3700 
  3701                     tableName = getDefine(tableName);
  3702                     if (ts.moveNext()) {
  3703                         tmp = ts.token();
  3704                         if ((tmp.id() == PlsqlTokenId.DOT) && ts.moveNext()) {
  3705                             tmp = ts.token();
  3706                         }
  3707                     }
  3708                 }
  3709             } else {
  3710                 ts.move(beginOffset);
  3711                 ts.moveNext();
  3712                 ts.moveNext();
  3713                 return block;
  3714             }
  3715         }
  3716 
  3717         while (moveNext) {
  3718             PlsqlTokenId tokenID = tmp.id();
  3719             String image = tmp.text().toString();
  3720 
  3721             //Check end offset and break (exception for colun which will mark end of some blocks)
  3722             if ((tmp != null) && (tmp.offset(tokenHierarchy) > endParse) && (!image.equals(";")) && (!(image.equals("/") && checkForOnlyChar(ts, ts.offset())))) {
  3723                 ts.move(beginOffset);
  3724                 ts.moveNext();
  3725                 ts.moveNext();
  3726                 break;
  3727             }
  3728 
  3729             //We have come to the end of the comment
  3730             if ((tokenID == PlsqlTokenId.OPERATOR)
  3731                     && (image.equals(";") || (image.equals("/") && checkForOnlyChar(ts, ts.offset())))) {
  3732                 isEnd = true;
  3733                 previousBlock = tmp;
  3734                 int offset = ts.offset();
  3735                 boolean mNext = getNextNonWhitespace(ts, false); //after ';' dont ignore comments
  3736 
  3737                 Token<PlsqlTokenId> next = ts.token();
  3738                 ts.move(offset);
  3739                 ts.moveNext();
  3740 
  3741                 if ((mNext == false) || (!next.text().toString().equalsIgnoreCase("COMMENT"))
  3742                         || ((next.text().toString().equalsIgnoreCase("COMMENT")) && (next.offset(tokenHierarchy) > endParse))) {                  //we have come to the end of the comments
  3743 
  3744                     if (isTable) {
  3745                         block = new PlsqlBlock(commentBegin.offset(tokenHierarchy),
  3746                                 previousBlock.offset(tokenHierarchy), tableName, alias, PlsqlBlockType.TABLE_COMMENT);
  3747                     } else {
  3748                         block = new PlsqlBlock(commentBegin.offset(tokenHierarchy),
  3749                                 previousBlock.offset(tokenHierarchy), tableName, alias, PlsqlBlockType.COLUMN_COMMENT);
  3750                     }
  3751 
  3752                     break;
  3753                 }
  3754             } else if ((tokenID == PlsqlTokenId.KEYWORD)) {
  3755                 if (image.equalsIgnoreCase("COLUMN")) {
  3756                     if (isTable == true) {
  3757                         isTable = false;
  3758 
  3759                         if (previousBlock != null) {
  3760                             //Create table comment fold
  3761                             block = new PlsqlBlock(commentBegin.offset(tokenHierarchy),
  3762                                     previousBlock.offset(tokenHierarchy), tableName, alias, PlsqlBlockType.TABLE_COMMENT);
  3763                             ts.move(tmpPre.offset(tokenHierarchy));
  3764                             break;
  3765                         }
  3766                     }
  3767                 } else if (image.equalsIgnoreCase("TABLE")) {
  3768                     if (isTable == false) {
  3769                         isTable = true;
  3770 
  3771                         if (previousBlock != null) {
  3772                             //Create column comment fold
  3773                             block = new PlsqlBlock(commentBegin.offset(tokenHierarchy),
  3774                                     previousBlock.offset(tokenHierarchy), tableName, alias, PlsqlBlockType.COLUMN_COMMENT);
  3775                             ts.move(tmpPre.offset(tokenHierarchy));
  3776                             break;
  3777                         }
  3778                     }
  3779                 } else if (image.equalsIgnoreCase("COMMENT")) {
  3780                     tmpPre = tmp;
  3781                     moveNext = getNextNonWhitespace(ts, true);
  3782                     tmp = ts.token();
  3783                     if (isEnd == false) {
  3784                         commentBegin = tmpPre;
  3785                     }
  3786                 }
  3787             } else if (tokenID == PlsqlTokenId.LINE_COMMENT) {
  3788                 //only single comment line
  3789                 if (image.toUpperCase(Locale.ENGLISH).contains("<FOLD>")) {
  3790                     customStartToken = tmp;
  3791                 } else if (image.toUpperCase(Locale.ENGLISH).contains("<END-FOLD>")) {
  3792                     if (customStartToken != null) {
  3793                         String name = customStartToken.text().toString();
  3794                         int index = name.toUpperCase(Locale.ENGLISH).indexOf("<FOLD>");
  3795                         name = name.substring(index + 7).trim();
  3796                         if (ts.moveNext()) {
  3797                             tmp = ts.token();
  3798                             PlsqlBlock custom = new PlsqlBlock(customStartToken.offset(tokenHierarchy),
  3799                                     tmp.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
  3800                             customFoldBlocks.add(custom);
  3801                         }
  3802 
  3803                         customStartToken = null;
  3804                     }
  3805                 } else {
  3806                     PlsqlBlock child = checkComment(tmp, ts);
  3807                     if (child != null) {
  3808                         if (checkExisting(child, lstChild) == false) {
  3809                             lstChild.add(child);
  3810                         }
  3811                     }
  3812                 }
  3813             } else if (tokenID == PlsqlTokenId.BLOCK_COMMENT) {
  3814                 int start = tmp.offset(tokenHierarchy);
  3815                 PlsqlBlock child = new PlsqlBlock(start,
  3816                         start + tmp.length(), "BLOCK COMMENT", "", PlsqlBlockType.COMMENT);
  3817                 if (child != null) {
  3818                     if (checkExisting(child, lstChild) == false) {
  3819                         lstChild.add(child);
  3820                     }
  3821                 }
  3822             }
  3823 
  3824             moveNext = getNextNonWhitespace(ts, true);
  3825             tmp = ts.token();
  3826         }
  3827 
  3828         if (block != null) {
  3829             //add children
  3830             addChildren(block, lstChild, parentBlocks);
  3831         }
  3832 
  3833         return block;
  3834     }
  3835 
  3836     /**
  3837      * Get Return next non whitespace token
  3838      *
  3839      * @param ts
  3840      * @param ignoreComment: if true will ignore comments also
  3841      * @return
  3842      */
  3843     private boolean getNextNonWhitespace(TokenSequence<PlsqlTokenId> ts, boolean ignoreComment) {
  3844         boolean moveNext = ts.moveNext();
  3845         Token<PlsqlTokenId> tmp = ts.token();
  3846 
  3847 
  3848         while (moveNext) {
  3849             if (tmp.id() == PlsqlTokenId.WHITESPACE) {
  3850                 moveNext = ts.moveNext();
  3851                 tmp = ts.token();
  3852             } else {
  3853                 if ((ignoreComment == true) && (tmp.id() == PlsqlTokenId.LINE_COMMENT
  3854                         || tmp.id() == PlsqlTokenId.BLOCK_COMMENT)) {
  3855                     moveNext = ts.moveNext();
  3856                     tmp = ts.token();
  3857                 } else {
  3858                     break;
  3859                 }
  3860             }
  3861         }
  3862 
  3863         return moveNext;
  3864     }
  3865 
  3866     /**
  3867      * Get Return next non whitespace token
  3868      *
  3869      * @param ts
  3870      * @return
  3871      */
  3872     private boolean getNextNonWhitespaceForComments(TokenSequence<PlsqlTokenId> ts) {
  3873         boolean moveNext = ts.moveNext();
  3874         Token<PlsqlTokenId> tmp = ts.token();
  3875         LOG.log(Level.FINE, "getNextNonWhitespaceForComments, tmp.id()={0}, tmp.text()={1}", new Object[]{tmp.id(), tmp.text().toString()});
  3876         while (moveNext) {
  3877             if (tmp.id() == PlsqlTokenId.WHITESPACE && ("\n".equals(tmp.text()) || tmp.text().toString().contains("\n "))) {
  3878                 moveNext = ts.moveNext();
  3879                 tmp = ts.token();
  3880             } else {
  3881                 break;
  3882             }
  3883         }
  3884         return moveNext;
  3885     }
  3886 
  3887     /**
  3888      * Return previous non whitespace token
  3889      *
  3890      * @param ts
  3891      * @param ignoreComment
  3892      * @return
  3893      */
  3894     private boolean getPreviousNonWhitespace(TokenSequence<PlsqlTokenId> ts, boolean ignoreComment) {
  3895         boolean movePrevious = ts.movePrevious();
  3896         Token<PlsqlTokenId> tmp = ts.token();
  3897 
  3898         while (movePrevious) {
  3899             if (tmp.id() == PlsqlTokenId.WHITESPACE) {
  3900                 movePrevious = ts.movePrevious();
  3901                 tmp = ts.token();
  3902             } else {
  3903                 if ((ignoreComment == true) && (tmp.id() == PlsqlTokenId.LINE_COMMENT
  3904                         || tmp.id() == PlsqlTokenId.BLOCK_COMMENT)) {
  3905                     movePrevious = ts.movePrevious();
  3906                     tmp = ts.token();
  3907                 } else {
  3908                     break;
  3909                 }
  3910             }
  3911         }
  3912         return movePrevious;
  3913     }
  3914 
  3915     /**
  3916      * Check whether this is the start of a VIEW block
  3917      *
  3918      * @param viewToken
  3919      * @param ts
  3920      * @param parentBlocks
  3921      * @return
  3922      */
  3923     private PlsqlBlock checkView(Token<PlsqlTokenId> viewToken, TokenSequence<PlsqlTokenId> ts, List<PlsqlBlock> parentBlocks) {
  3924         Token<PlsqlTokenId> viewBegin = viewToken;
  3925         Token<PlsqlTokenId> tmp = viewToken;
  3926         PlsqlBlock block = null;
  3927         List<PlsqlBlock> lstChild = new ArrayList<PlsqlBlock>();
  3928 
  3929         boolean moveNext = false;
  3930         String viewName = "";
  3931         int offset = ts.offset();
  3932 
  3933         //Check whether the beginning is in a SQL Plus command
  3934         if (sqlPlusLine(ts)) {
  3935             return null;
  3936         }
  3937 
  3938         //second non whitespace character should be the name
  3939         moveNext = getNextNonWhitespace(ts, true);
  3940         if (moveNext == false) {
  3941             ts.move(offset);
  3942             ts.moveNext();
  3943             return block;
  3944         }
  3945 
  3946         tmp = ts.token();
  3947 
  3948         //Second token is the view name
  3949         viewName = tmp.text().toString().trim();
  3950         viewName = checkForOtherSchema(ts, viewName);
  3951         String alias = "";
  3952         if (viewName.indexOf('&') != -1) {
  3953             alias = viewName;
  3954             viewName = getDefine(viewName);
  3955         }
  3956 //      if (ts.moveNext()) {
  3957 //         tmp = ts.token();
  3958 //         if ((tmp.id() == PlsqlTokenId.DOT) && ts.moveNext()) {
  3959 //            tmp = ts.token();
  3960 //            viewName = viewName + "." + tmp.toString();
  3961 //
  3962 //            //Move to next token
  3963 //            moveNext = getNextNonWhitespace(ts, true);
  3964 //            tmp = ts.token();
  3965 //         }
  3966 //      }
  3967 
  3968         Token<PlsqlTokenId> customStartToken = null;
  3969         boolean isOk = false;
  3970 
  3971         while (moveNext) {
  3972             String image = tmp.text().toString();
  3973             PlsqlTokenId tokenID = tmp.id();
  3974 
  3975             //Check end offset and break(exception for colun which will mark end of some blocks)
  3976             if ((tmp != null) && (tmp.offset(tokenHierarchy) > endParse) && (!image.equals(";"))) {
  3977                 break;
  3978             }
  3979 
  3980             if ((tokenID == PlsqlTokenId.KEYWORD) && (image.equalsIgnoreCase("AS"))) {
  3981                 isOk = true;
  3982             }
  3983 
  3984             //We have come to the end of the view declaration
  3985             if ((tokenID == PlsqlTokenId.OPERATOR)
  3986                     && (image.equals(";") || (image.equals("/") && checkForOnlyChar(ts, ts.offset())))) {
  3987                 if (isOk) {
  3988 //               String alias = "";
  3989 //               if (viewName.indexOf('&') != -1) {
  3990 //                  int dotIndex = viewName.indexOf('.');
  3991 //                  if (dotIndex != -1) {
  3992 //                     String firstPart = viewName.substring(0, dotIndex);
  3993 //                     alias = firstPart;
  3994 //                     viewName = getDefine(firstPart) + viewName.substring(dotIndex + 1);
  3995 //                  } else {
  3996 //                     alias = viewName;
  3997 //                     viewName = getDefine(viewName);
  3998 //                  }
  3999 //               }
  4000 
  4001                     block = new PlsqlBlock(viewBegin.offset(tokenHierarchy),
  4002                             tmp.offset(tokenHierarchy), viewName, alias, PlsqlBlockType.VIEW);
  4003                     checkPrefix(viewBegin.offset(tokenHierarchy), ts, block);
  4004                     break;
  4005                 } else {
  4006                     ts.move(offset);
  4007                     ts.moveNext();
  4008                     ts.moveNext(); // to avoid getting caught here again we have to move next
  4009                     break;
  4010                 }
  4011             } else if ((tokenID == PlsqlTokenId.KEYWORD)
  4012                     && ((image.equalsIgnoreCase("COMMENT"))
  4013                     || (image.equalsIgnoreCase("CREATE")))) { //Avoid catching ';' of other statements
  4014 
  4015                 ts.move(offset);
  4016                 ts.moveNext();
  4017                 ts.moveNext(); // to avoid getting caught here again we have to move next
  4018 
  4019                 break;
  4020 
  4021             } else if (tokenID == PlsqlTokenId.LINE_COMMENT) {
  4022                 //only single comment line
  4023                 if (image.toUpperCase(Locale.ENGLISH).contains("<FOLD>")) {
  4024                     customStartToken = tmp;
  4025                 } else if (image.toUpperCase(Locale.ENGLISH).contains("<END-FOLD>")) {
  4026                     if (customStartToken != null) {
  4027                         String name = customStartToken.text().toString();
  4028                         int index = name.toUpperCase(Locale.ENGLISH).indexOf("<FOLD>");
  4029                         name = name.substring(index + 7).trim();
  4030                         if (ts.moveNext()) {
  4031                             tmp = ts.token();
  4032                             PlsqlBlock custom = new PlsqlBlock(customStartToken.offset(tokenHierarchy),
  4033                                     tmp.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
  4034                             customFoldBlocks.add(custom);
  4035                         }
  4036 
  4037                         customStartToken = null;
  4038                     }
  4039                 } else {
  4040                     PlsqlBlock child = checkComment(tmp, ts);
  4041                     if (child != null) {
  4042                         if (checkExisting(child, lstChild) == false) {
  4043                             lstChild.add(child);
  4044                         }
  4045                     }
  4046                 }
  4047                 moveNext = ts.moveNext();
  4048                 tmp = ts.token();
  4049             } else if (tokenID == PlsqlTokenId.BLOCK_COMMENT) {
  4050                 int start = tmp.offset(tokenHierarchy);
  4051                 PlsqlBlock child = new PlsqlBlock(start,
  4052                         start + tmp.length(), "BLOCK COMMENT", "", PlsqlBlockType.COMMENT);
  4053                 if (child != null) {
  4054                     if (checkExisting(child, lstChild) == false) {
  4055                         lstChild.add(child);
  4056                     }
  4057                 }
  4058                 moveNext = ts.moveNext();
  4059                 tmp = ts.token();
  4060             } else {
  4061                 moveNext = ts.moveNext();
  4062                 tmp = ts.token();
  4063             }
  4064         }
  4065 
  4066         if (block != null) {
  4067             //add children
  4068             addChildren(block, lstChild, parentBlocks);
  4069         }
  4070 
  4071         return block;
  4072     }
  4073 
  4074     /**
  4075      * Get the name defined by &Name
  4076      *
  4077      * @param inputName
  4078      * @return
  4079      */
  4080     public String getDefine(
  4081             String inputName) {
  4082         String name = inputName;
  4083 
  4084         if (name.indexOf('&', 0) != -1) {
  4085             String val = definesMap.get(name.substring(1).toUpperCase(Locale.ENGLISH));
  4086             if (val != null) {
  4087                 name = val;
  4088             }
  4089         }
  4090 
  4091         return name;
  4092     }
  4093 
  4094     /**
  4095      * Check &Name is in map as a key
  4096      *
  4097      * @param inputName
  4098      * @return
  4099      */
  4100     public boolean isDefine(String inputName) {
  4101         String name = inputName;
  4102 
  4103         if (name.indexOf('&', 0) != -1) {
  4104             return definesMap.containsKey(name.substring(1).toUpperCase(Locale.ENGLISH));
  4105         }
  4106 
  4107         return false;
  4108     }
  4109 
  4110     /**
  4111      * Check &Name is in map as a value
  4112      *
  4113      * @param inputName
  4114      * @return
  4115      */
  4116     public boolean hasDefineKey(String inputName) {
  4117         String name = inputName;
  4118         return definesMap.containsValue(name.toUpperCase(Locale.ENGLISH));
  4119     }
  4120 
  4121     /**
  4122      * Get the key of &Name
  4123      *
  4124      * @param inputName
  4125      * @return
  4126      */
  4127     public String getDefineKey(String inputName) {
  4128         for (Object o : definesMap.keySet()) {
  4129             if (definesMap.get(o).equals(inputName)) {
  4130                 return o.toString();
  4131             }
  4132         }
  4133         return null;
  4134     }
  4135 
  4136     public Map<String, String> getDefines() {
  4137         return Collections.unmodifiableMap(definesMap);
  4138     }
  4139 
  4140     /**
  4141      * Method that will parse the document and initialize the aliases
  4142      *
  4143      * @param doc
  4144      */
  4145     private void getAliases(Document doc) {
  4146         TokenHierarchy tokenHier = TokenHierarchy.get(doc);
  4147         @SuppressWarnings("unchecked")
  4148         TokenSequence<PlsqlTokenId> ts = tokenHier.tokenSequence(PlsqlTokenId.language());
  4149         if (ts == null) {
  4150             return;
  4151         }
  4152 
  4153         //start to check aliases from the previous line end
  4154         ts.move(startParse);
  4155         Token<PlsqlTokenId> token = ts.token();
  4156 
  4157         //Get the difine by the name
  4158         while (ts.moveNext() && ts.offset() <= endParse) {
  4159             token = ts.token();
  4160             //Check whether this is DEFINE
  4161             if (token.id() == PlsqlTokenId.SQL_PLUS
  4162                     && (token.toString().equalsIgnoreCase("DEF")
  4163                     || token.toString().equalsIgnoreCase("DEFI")
  4164                     || token.toString().equalsIgnoreCase("DEFIN")
  4165                     || token.toString().equalsIgnoreCase("DEFINE"))) {
  4166                 String tokenTxt = readLine(ts, token);
  4167                 if (!tokenTxt.contains(" = ") && tokenTxt.contains("=")) {
  4168                     tokenTxt = tokenTxt.substring(0, tokenTxt.indexOf("=")) + " = " + tokenTxt.substring(tokenTxt.indexOf("=") + 1);
  4169                 }
  4170 
  4171                 StringTokenizer tokenizer = new StringTokenizer(tokenTxt);
  4172                 tokenizer.nextToken();
  4173                 String alias;
  4174                 String value = "";
  4175                 boolean isNext = tokenizer.hasMoreTokens();
  4176 
  4177                 //alias
  4178                 if (isNext) {
  4179                     alias = tokenizer.nextToken();
  4180                 } else {
  4181                     break;
  4182                 }
  4183 
  4184                 isNext = tokenizer.hasMoreTokens();
  4185 
  4186                 if ((isNext) && (tokenizer.nextToken().equals("="))) {
  4187                     boolean isComment = false;
  4188                     while (tokenizer.hasMoreTokens() && !isComment) {
  4189                         String temp = tokenizer.nextToken();
  4190                         if (temp.startsWith("--") || temp.startsWith("/*")) {
  4191                             isComment = true;
  4192                         } else {
  4193                             value = value + " " + temp;
  4194                         }
  4195                     }
  4196 
  4197                     value = value.trim();
  4198 
  4199                     if ((value.startsWith("\"") && value.endsWith("\""))
  4200                             || (value.startsWith("\'") && value.endsWith("\'"))) {
  4201                         value = value.substring(1, value.length() - 1);
  4202                     }
  4203 
  4204                     definesMap.put(alias.toUpperCase(Locale.ENGLISH), value);
  4205                 }
  4206             }
  4207         }
  4208     }
  4209 
  4210     /**
  4211      * Replace exStr in the given text with newStr
  4212      *
  4213      * @param plsqlString
  4214      * @param exStr
  4215      * @param newStr
  4216      * @return
  4217      */
  4218     public String replaceText(
  4219             String plsqlString, String exStr, String newStr) {
  4220         if (plsqlString.indexOf(exStr) >= 0) {
  4221             plsqlString = plsqlString.replace(exStr, newStr);
  4222         }
  4223 
  4224         return plsqlString;
  4225     }
  4226 
  4227     /**
  4228      * Check whether the given offsets are in the same line
  4229      *
  4230      * @param doc
  4231      * @param offset1
  4232      * @param offset2
  4233      * @return
  4234      */
  4235     private boolean checkSameLine(Document doc, int offset1, int offset2) {
  4236         int startLine = offset2;
  4237         int endLine = offset2;
  4238 
  4239         TokenHierarchy tokenHier = TokenHierarchy.get(doc);
  4240         @SuppressWarnings("unchecked")
  4241         TokenSequence<PlsqlTokenId> ts = tokenHier.tokenSequence(PlsqlTokenId.language());
  4242         if (ts == null) {
  4243             return false;
  4244         }
  4245 
  4246         //go to the previous line break
  4247         ts.move(offset2);
  4248         boolean movePrevious = ts.movePrevious();
  4249         Token<PlsqlTokenId> tokenPre = ts.token();
  4250 
  4251         while (movePrevious) {
  4252             if (tokenPre.text().toString().contains("\n")) {
  4253                 startLine = tokenPre.offset(tokenHier);
  4254                 break;
  4255 
  4256             }
  4257             movePrevious = ts.movePrevious();
  4258             tokenPre = ts.token();
  4259         }
  4260 
  4261         //If cannot move previous and start line not set it is the document begin
  4262         if ((startLine == offset2) && (!movePrevious)) {
  4263             startLine = doc.getStartPosition().getOffset();
  4264         }
  4265 
  4266         //go to the next line break
  4267         ts.move(offset2);
  4268         boolean moveNext = ts.moveNext();
  4269         Token<PlsqlTokenId> tokenNext = ts.token();
  4270 
  4271         while (moveNext) {
  4272             if (tokenNext.text().toString().contains("\n")) {
  4273                 endLine = tokenNext.offset(tokenHier);
  4274                 break;
  4275             }
  4276 
  4277             moveNext = ts.moveNext();
  4278             tokenNext = ts.token();
  4279         }
  4280 
  4281         //If cannot move next and end line not set it is the document end
  4282         if ((endLine == offset2) && (!moveNext)) {
  4283             endLine = doc.getEndPosition().getOffset();
  4284         }
  4285 
  4286         if ((offset1 >= startLine) && (offset1 <= endLine)) {
  4287             if ((offset2 >= startLine) && (offset2 <= endLine)) {
  4288                 return true;
  4289             }
  4290         }
  4291 
  4292         return false;
  4293     }
  4294 
  4295     /**
  4296      * Method that will check if blocks
  4297      *
  4298      * @param current
  4299      * @param ts
  4300      * @param parentBlocks
  4301      * @return
  4302      */
  4303     private List<PlsqlBlock> checkIfBlock(Token<PlsqlTokenId> current, TokenSequence<PlsqlTokenId> ts, List<PlsqlBlock> parentBlocks) {
  4304         Token<PlsqlTokenId> ifBegin = null;
  4305         Token<PlsqlTokenId> token = null;
  4306         int preOffset = -1;
  4307         List<PlsqlBlock> ifBlocks = new ArrayList<PlsqlBlock>();
  4308         List<PlsqlBlock> lstChild = new ArrayList<PlsqlBlock>();
  4309         boolean moveNext = false;
  4310 
  4311         //Check whether the beginning is in a SQL Plus command
  4312         if (sqlPlusLine(ts)) {
  4313             return null;
  4314         }
  4315 
  4316         moveNext = ts.moveNext();
  4317         token = ts.token();
  4318         ifBegin = current;
  4319         String name = ifBegin.text().toString() + " ";
  4320         boolean isThen = false;
  4321         Token<PlsqlTokenId> customStartToken = null;
  4322         //If this is an else check we need to ignore then
  4323         if (name.trim().equalsIgnoreCase("ELSE")) {
  4324             isThen = true;
  4325         }
  4326         while (moveNext) {
  4327             String image = token.text().toString();
  4328             PlsqlTokenId tokenID = token.id();
  4329 
  4330             if ((token != null) && (!image.equals(";")) && (token.offset(tokenHierarchy) > endParse)) {
  4331                 break;
  4332             }
  4333 
  4334             if (image.equalsIgnoreCase("ELSE")) {
  4335                 if (isThen) {
  4336                     PlsqlBlock block = new PlsqlBlock(ifBegin.offset(tokenHierarchy), preOffset, name.trim(), "", PlsqlBlockType.IF);
  4337                     if (block != null) {
  4338                         //add children
  4339                         addChildren(block, lstChild, parentBlocks);
  4340                         ifBlocks.add(block);
  4341                         lstChild.clear();
  4342                     }
  4343 
  4344                     name = "ELSE";
  4345                     ifBegin = token;
  4346                 } else {
  4347                     break;
  4348                 }
  4349             } else if (image.equalsIgnoreCase("ELSIF")) {
  4350                 if (isThen) {
  4351                     PlsqlBlock block = new PlsqlBlock(ifBegin.offset(tokenHierarchy), preOffset, name.trim(), "", PlsqlBlockType.IF);
  4352                     if (block != null) {
  4353                         //add children
  4354                         addChildren(block, lstChild, parentBlocks);
  4355                         ifBlocks.add(block);
  4356                         lstChild.clear();
  4357                     }
  4358                     //reset everything for ELSE IF
  4359                     name = "ELSIF";
  4360                     isThen = false;
  4361                     ifBegin = token;
  4362                 } else {
  4363                     break;
  4364                 }
  4365             } else if (image.equalsIgnoreCase("END")) {
  4366                 if (isThen) {
  4367                     boolean next = getNextNonWhitespace(ts, true);
  4368                     Token<PlsqlTokenId> nextTok = ts.token();
  4369                     if (next && nextTok.text().toString().equalsIgnoreCase("IF")) {
  4370                         next = getNextNonWhitespace(ts, true);
  4371                         nextTok = ts.token();
  4372                         if (next && nextTok.text().toString().equalsIgnoreCase(";")) {
  4373                             ts.moveNext();
  4374                             PlsqlBlock block = new PlsqlBlock(ifBegin.offset(tokenHierarchy), ts.offset(), name.trim(), "", PlsqlBlockType.IF);
  4375                             if (block != null) {
  4376                                 //add children
  4377                                 addChildren(block, lstChild, parentBlocks);
  4378                                 ifBlocks.add(block);
  4379                                 lstChild.clear();
  4380                                 break;
  4381                             }
  4382                         } else {
  4383                             break;
  4384                         }
  4385                     }
  4386                 } else {
  4387                     break;
  4388                 }
  4389             } else if (image.equalsIgnoreCase("THEN")) {
  4390                 isThen = true;
  4391             } else if (image.equalsIgnoreCase("IF")) {
  4392                 int beforeOff = token.offset(tokenHierarchy);
  4393                 List children = checkIfBlock(token, ts, lstChild);
  4394                 if (children == null || children.isEmpty()) {//If inner check seems to have failed need to continue this one
  4395 
  4396                     ts.move(beforeOff);
  4397                     moveNext = ts.moveNext();
  4398                 } else {
  4399                     for (int i = 0; i < children.size(); i++) {
  4400                         PlsqlBlock child = (PlsqlBlock) children.get(i);
  4401                         if (checkExisting(child, lstChild) == false) {
  4402                             lstChild.add(child);
  4403                         }
  4404                     }
  4405                 }
  4406             } else if (image.equalsIgnoreCase("CASE")) {
  4407                 int beforeOff = token.offset(tokenHierarchy);
  4408                 List children = checkCaseBlock(token, ts, lstChild, false);
  4409                 if (children == null || children.isEmpty()) {//If inner check seems to have failed need to continue this one
  4410 
  4411                     ts.move(beforeOff);
  4412                     moveNext = ts.moveNext();
  4413                 } else {
  4414                     for (int i = 0; i < children.size(); i++) {
  4415                         PlsqlBlock child = (PlsqlBlock) children.get(i);
  4416                         if (checkExisting(child, lstChild) == false) {
  4417                             lstChild.add(child);
  4418                         }
  4419                     }
  4420                 }
  4421             } else if (tokenID == PlsqlTokenId.KEYWORD && image.equalsIgnoreCase("DECLARE")) {
  4422                 PlsqlBlock child = checkDeclareBlock(token, ts, lstChild);
  4423                 if (child != null) {//If inner check seems to have failed need to continue this one
  4424                     if (checkExisting(child, lstChild) == false) {
  4425                         lstChild.add(child);
  4426                     }
  4427                 }
  4428             } else if (tokenID == PlsqlTokenId.KEYWORD && (image.equalsIgnoreCase("BEGIN"))) {
  4429                 int beforeOff = token.offset(tokenHierarchy);
  4430                 PlsqlBlock child = checkBeginBlock(token, ts, lstChild);
  4431                 if (child == null) {//If inner check seems to have failed need to continue this one
  4432 
  4433                     ts.move(beforeOff);
  4434                     moveNext = ts.moveNext();
  4435                 } else {
  4436                     if (checkExisting(child, lstChild) == false) {
  4437                         lstChild.add(child);
  4438                     }
  4439                 }
  4440             } else if (image.equalsIgnoreCase("WHILE")
  4441                     || image.equalsIgnoreCase("FOR")
  4442                     || image.equalsIgnoreCase("LOOP")) {
  4443                 int beforeOff = token.offset(tokenHierarchy);
  4444                 if (!unsuccessBlocks.contains(beforeOff)) {
  4445                     PlsqlBlock child = checkLoopBlock(token, ts, lstChild);
  4446                     if (child == null) {//If inner check seems to have failed need to continue this one
  4447                         unsuccessBlocks.add(beforeOff);
  4448                         ts.move(beforeOff);
  4449                         moveNext = ts.moveNext();
  4450                     } else {
  4451                         if (checkExisting(child, lstChild) == false) {
  4452                             lstChild.add(child);
  4453                         }
  4454                     }
  4455                 }
  4456             } else if (image.equalsIgnoreCase("TABLE")
  4457                     || image.equalsIgnoreCase("INDEX")
  4458                     || image.equalsIgnoreCase("SELECT")
  4459                     || image.equalsIgnoreCase("UPDATE")
  4460                     || image.equalsIgnoreCase("DELETE")
  4461                     || image.equalsIgnoreCase("INSERT")
  4462                     || image.equalsIgnoreCase("MERGE")
  4463                     || image.equalsIgnoreCase("DROP")
  4464                     || image.equalsIgnoreCase("SEQUENCE")) {
  4465                 if (!isNotBlockStart(token, ts)) {
  4466                     int offset = token.offset(tokenHierarchy);
  4467                     PlsqlBlock child = checkStatementBlock(token, ts, parentBlocks);
  4468                     if (child == null) {//If inner check seems to have failed need to continue this one
  4469 
  4470                         ts.move(offset);
  4471                         ts.moveNext();
  4472                     } else {
  4473                         if (checkExisting(child, lstChild) == false) {
  4474                             lstChild.add(child);
  4475                         }
  4476                     }
  4477                 }
  4478             } else if (image.equalsIgnoreCase("PROCEDURE")
  4479                     || image.equalsIgnoreCase("FUNCTION")
  4480                     || image.equalsIgnoreCase("CREATE")) {
  4481                 break;
  4482             } else if (tokenID == PlsqlTokenId.LINE_COMMENT) {
  4483                 //only single comment line
  4484                 if (image.toUpperCase(Locale.ENGLISH).contains("<FOLD>")) {
  4485                     customStartToken = token;
  4486                 } else if (image.toUpperCase(Locale.ENGLISH).contains("<END-FOLD>")) {
  4487                     if (customStartToken != null) {
  4488                         String fname = customStartToken.text().toString();
  4489                         int index = fname.toUpperCase(Locale.ENGLISH).indexOf("<FOLD>");
  4490                         fname = fname.substring(index + 7).trim();
  4491                         if (ts.moveNext()) {
  4492                             token = ts.token();
  4493                             PlsqlBlock custom = new PlsqlBlock(customStartToken.offset(tokenHierarchy),
  4494                                     token.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
  4495                             customFoldBlocks.add(custom);
  4496                         }
  4497                         customStartToken = null;
  4498                     }
  4499                 } else {
  4500                     PlsqlBlock child = checkComment(token, ts);
  4501                     if (child != null) {
  4502                         if (checkExisting(child, lstChild) == false) {
  4503                             lstChild.add(child);
  4504                         }
  4505                     }
  4506                 }
  4507             } else if (tokenID == PlsqlTokenId.BLOCK_COMMENT) {
  4508                 int start = token.offset(tokenHierarchy);
  4509                 PlsqlBlock child = new PlsqlBlock(start,
  4510                         start + token.length(), "BLOCK COMMENT", "", PlsqlBlockType.COMMENT);
  4511                 if (child != null) {
  4512                     if (checkExisting(child, lstChild) == false) {
  4513                         lstChild.add(child);
  4514                     }
  4515                 }
  4516             } else if (!isThen) {
  4517                 name = name + image;
  4518             }
  4519 
  4520             preOffset = ts.offset();
  4521             moveNext = ts.moveNext();
  4522             token = ts.token();
  4523         }
  4524 
  4525         return ifBlocks;
  4526     }
  4527 
  4528     /**
  4529      * Method that will check case blocks
  4530      *
  4531      * @param current
  4532      * @param ts
  4533      * @param parentBlocks
  4534      * @return
  4535      */
  4536     private List<PlsqlBlock> checkCaseBlock(Token<PlsqlTokenId> current, TokenSequence<PlsqlTokenId> ts, List<PlsqlBlock> parentBlocks, boolean isStatement) {
  4537         Token<PlsqlTokenId> caseBegin = null;
  4538         Token<PlsqlTokenId> token = null;
  4539         int preOffset = -1;
  4540         List<PlsqlBlock> caseBlocks = new ArrayList<PlsqlBlock>();
  4541         List<PlsqlBlock> lstChild = new ArrayList<PlsqlBlock>();
  4542         boolean moveNext = false;
  4543 
  4544         //Check whether the beginning is in a SQL Plus command
  4545         if (!isStatement && sqlPlusLine(ts)) {
  4546             return null;
  4547         }
  4548 
  4549         moveNext = ts.moveNext();
  4550         token = ts.token();
  4551         caseBegin = current;
  4552         String name = caseBegin.text().toString() + " ";
  4553         boolean isThen = false;
  4554         Token<PlsqlTokenId> customStartToken = null;
  4555         //If this is an else check we need to ignore then
  4556         if (name.trim().equalsIgnoreCase("ELSE")) {
  4557             isThen = true;
  4558         }
  4559 
  4560         while (moveNext) {
  4561             String image = token.text().toString();
  4562             PlsqlTokenId tokenID = token.id();
  4563 
  4564             if ((token != null) && (!image.equals(";")) && (token.offset(tokenHierarchy) > endParse)) {
  4565                 break;
  4566             }
  4567 
  4568             if (image.equalsIgnoreCase("ELSE")) {
  4569                 if (isThen) {
  4570                     PlsqlBlock block = new PlsqlBlock(caseBegin.offset(tokenHierarchy), preOffset, name.trim(), "", PlsqlBlockType.CASE);
  4571                     if (block != null) {
  4572                         //add children
  4573                         addChildren(block, lstChild, parentBlocks);
  4574                         caseBlocks.add(block);
  4575                         lstChild.clear();
  4576                     }
  4577 
  4578                     name = "ELSE";
  4579                     caseBegin = token;
  4580                 } else {
  4581                     break;
  4582                 }
  4583             } else if (image.equalsIgnoreCase("WHEN")) {
  4584                 if (isThen) {
  4585                     PlsqlBlock block = new PlsqlBlock(caseBegin.offset(tokenHierarchy), preOffset, name.trim(), "", PlsqlBlockType.CASE);
  4586                     if (block != null) {
  4587                         //add children
  4588                         addChildren(block, lstChild, parentBlocks);
  4589                         caseBlocks.add(block);
  4590                         lstChild.clear();
  4591                     }
  4592                     //reset everything for ELSE IF
  4593                     name = "WHEN";
  4594                     isThen = false;
  4595                     caseBegin = token;
  4596                 } else if (name.trim().startsWith("CASE")) { //first WHEN
  4597                     name = name + image;
  4598                 } else {
  4599                     break;
  4600                 }
  4601             } else if (image.equalsIgnoreCase("END")) {
  4602                 if (isThen) {
  4603                     boolean next = false;
  4604                     Token<PlsqlTokenId> nextTok = token;
  4605                     int offset = ts.offset();
  4606 
  4607                     next = getNextNonWhitespace(ts, true);
  4608                     nextTok = ts.token();
  4609                     if (next && nextTok.text().toString().equalsIgnoreCase("CASE")) {
  4610                         next = getNextNonWhitespace(ts, true);
  4611                         nextTok = ts.token();
  4612                     }
  4613                     if (!(!isStatement && next && nextTok.text().toString().equalsIgnoreCase(";"))) {
  4614                         ts.move(offset);
  4615                         ts.moveNext();
  4616                     }
  4617 
  4618                     ts.moveNext();
  4619                     PlsqlBlock block = new PlsqlBlock(caseBegin.offset(tokenHierarchy), ts.offset(), name.trim(), "", PlsqlBlockType.CASE);
  4620                     if (block != null) {
  4621                         //add children
  4622                         addChildren(block, lstChild, parentBlocks);
  4623                         caseBlocks.add(block);
  4624                         lstChild.clear();
  4625                         if (isStatement && ts.token().toString().equals(";")) {
  4626                             ts.movePrevious();
  4627                         }
  4628                         break;
  4629                     }
  4630                 } else {
  4631                     break;
  4632                 }
  4633             } else if (image.equalsIgnoreCase("THEN")) {
  4634                 isThen = true;
  4635             } else if (image.equalsIgnoreCase("IF")) {
  4636                 int beforeOff = token.offset(tokenHierarchy);
  4637                 List children = checkIfBlock(token, ts, lstChild);
  4638                 if (children == null || children.isEmpty()) {//If inner check seems to have failed need to continue this one
  4639 
  4640                     ts.move(beforeOff);
  4641                     moveNext = ts.moveNext();
  4642                 } else {
  4643                     for (int i = 0; i < children.size(); i++) {
  4644                         PlsqlBlock child = (PlsqlBlock) children.get(i);
  4645                         if (checkExisting(child, lstChild) == false) {
  4646                             lstChild.add(child);
  4647                         }
  4648                     }
  4649                 }
  4650             } else if (image.equalsIgnoreCase("CASE")) {
  4651                 int beforeOff = token.offset(tokenHierarchy);
  4652                 List children = checkCaseBlock(token, ts, lstChild, isStatement);
  4653                 if (children == null || children.isEmpty()) {//If inner check seems to have failed need to continue this one
  4654 
  4655                     ts.move(beforeOff);
  4656                     moveNext = ts.moveNext();
  4657                 } else {
  4658                     for (int i = 0; i < children.size(); i++) {
  4659                         PlsqlBlock child = (PlsqlBlock) children.get(i);
  4660                         if (checkExisting(child, lstChild) == false) {
  4661                             lstChild.add(child);
  4662                         }
  4663                     }
  4664                 }
  4665             } else if (image.equalsIgnoreCase("WHILE")
  4666                     || image.equalsIgnoreCase("FOR")
  4667                     || image.equalsIgnoreCase("LOOP")) {
  4668                 int beforeOff = token.offset(tokenHierarchy);
  4669                 if (!unsuccessBlocks.contains(beforeOff)) {
  4670                     PlsqlBlock child = checkLoopBlock(token, ts, lstChild);
  4671                     if (child == null) {//If inner check seems to have failed need to continue this one
  4672                         unsuccessBlocks.add(beforeOff);
  4673                         ts.move(beforeOff);
  4674                         moveNext = ts.moveNext();
  4675                     } else {
  4676                         if (checkExisting(child, lstChild) == false) {
  4677                             lstChild.add(child);
  4678                         }
  4679                     }
  4680                 }
  4681             } else if (tokenID == PlsqlTokenId.KEYWORD && (image.equalsIgnoreCase("BEGIN"))) {
  4682                 int beforeOff = token.offset(tokenHierarchy);
  4683                 PlsqlBlock child = checkBeginBlock(token, ts, lstChild);
  4684                 if (child == null) {//If inner check seems to have failed need to continue this one
  4685 
  4686                     ts.move(beforeOff);
  4687                     moveNext = ts.moveNext();
  4688                 } else {
  4689                     if (checkExisting(child, lstChild) == false) {
  4690                         lstChild.add(child);
  4691                     }
  4692                 }
  4693             } else if (image.equalsIgnoreCase("TABLE")
  4694                     || image.equalsIgnoreCase("INDEX")
  4695                     || image.equalsIgnoreCase("SELECT")
  4696                     || image.equalsIgnoreCase("UPDATE")
  4697                     || image.equalsIgnoreCase("DELETE")
  4698                     || image.equalsIgnoreCase("INSERT")
  4699                     || image.equalsIgnoreCase("MERGE")
  4700                     || image.equalsIgnoreCase("DROP")
  4701                     || image.equalsIgnoreCase("SEQUENCE")) {
  4702                 if (!isNotBlockStart(token, ts)) {
  4703                     int offset = token.offset(tokenHierarchy);
  4704                     PlsqlBlock child = checkStatementBlock(token, ts, parentBlocks);
  4705                     if (child == null) {//If inner check seems to have failed need to continue this one
  4706 
  4707                         ts.move(offset);
  4708                         ts.moveNext();
  4709                     } else {
  4710                         if (checkExisting(child, lstChild) == false) {
  4711                             lstChild.add(child);
  4712                         }
  4713                     }
  4714                 }
  4715             } else if (image.equalsIgnoreCase("PROCEDURE")
  4716                     || image.equalsIgnoreCase("FUNCTION")
  4717                     || image.equalsIgnoreCase("CREATE")) {
  4718                 break;
  4719             } else if (tokenID == PlsqlTokenId.LINE_COMMENT) {
  4720                 //only single comment line
  4721                 if (image.toUpperCase(Locale.ENGLISH).contains("<FOLD>")) {
  4722                     customStartToken = token;
  4723                 } else if (image.toUpperCase(Locale.ENGLISH).contains("<END-FOLD>")) {
  4724                     if (customStartToken != null) {
  4725                         if (ts.moveNext()) {
  4726                             token = ts.token();
  4727                             PlsqlBlock custom = new PlsqlBlock(customStartToken.offset(tokenHierarchy),
  4728                                     token.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
  4729                             customFoldBlocks.add(custom);
  4730                         }
  4731                         customStartToken = null;
  4732                     }
  4733                 } else {
  4734                     PlsqlBlock child = checkComment(token, ts);
  4735                     if (child != null) {
  4736                         if (checkExisting(child, lstChild) == false) {
  4737                             lstChild.add(child);
  4738                         }
  4739                     }
  4740                 }
  4741             } else if (tokenID == PlsqlTokenId.BLOCK_COMMENT) {
  4742                 int start = token.offset(tokenHierarchy);
  4743                 PlsqlBlock child = new PlsqlBlock(start,
  4744                         start + token.length(), "BLOCK COMMENT", "", PlsqlBlockType.COMMENT);
  4745                 if (checkExisting(child, lstChild) == false) {
  4746                     lstChild.add(child);
  4747                 }
  4748             } else if (!isThen) {
  4749                 name = name + image;
  4750             }
  4751 
  4752             preOffset = ts.offset();
  4753             moveNext = ts.moveNext();
  4754             token = ts.token();
  4755         }
  4756 
  4757         return caseBlocks;
  4758     }
  4759 
  4760     /**
  4761      * Method that will return the prefix of the given block
  4762      *
  4763      * @param startOffset
  4764      * @param ts
  4765      * @return
  4766      */
  4767     private String getPreceedingText(int startOffset, TokenSequence<PlsqlTokenId> ts) {
  4768         String prefix = "";
  4769         int offset = ts.offset();
  4770         ts.move(startOffset);
  4771         ts.moveNext();
  4772         Token<PlsqlTokenId> token = ts.token();
  4773 
  4774         while (ts.movePrevious()) {
  4775             token = ts.token();
  4776             String image = token.text().toString();
  4777 
  4778             if (image.contains("\n")) {
  4779                 break;
  4780             }
  4781 
  4782             prefix = token.text().toString() + prefix;
  4783         }
  4784 
  4785         ts.move(offset);
  4786         ts.moveNext();
  4787         return prefix;
  4788     }
  4789 
  4790     /**
  4791      * Method that will check loop blocks
  4792      *
  4793      * @param current
  4794      * @param ts
  4795      * @param parentBlocks
  4796      * @return
  4797      */
  4798     private PlsqlBlock checkLoopBlock(Token<PlsqlTokenId> current, TokenSequence<PlsqlTokenId> ts, List<PlsqlBlock> parentBlocks) {
  4799         Token<PlsqlTokenId> loopBegin = null;
  4800         Token<PlsqlTokenId> token = null;
  4801         List<PlsqlBlock> lstChild = new ArrayList<PlsqlBlock>();
  4802         PlsqlBlock block = null;
  4803         boolean moveNext = false;
  4804 
  4805         //Check whether the beginning is in a SQL Plus command
  4806         if (sqlPlusLine(ts)) {
  4807             return null;
  4808         }
  4809 
  4810         moveNext = ts.moveNext();
  4811         token = ts.token();
  4812         loopBegin = current;
  4813         boolean isLoop = false;
  4814         Token<PlsqlTokenId> customStartToken = null;
  4815         PlsqlBlockType type = PlsqlBlockType.LOOP;
  4816         String name = "";
  4817         if (loopBegin.text().toString().equalsIgnoreCase("LOOP")) {
  4818             isLoop = true;
  4819         } else if (loopBegin.text().toString().equalsIgnoreCase("WHILE")) {
  4820             type = PlsqlBlockType.WHILE_LOOP;
  4821         } else if (loopBegin.text().toString().equalsIgnoreCase("FOR")) {
  4822             type = PlsqlBlockType.FOR_LOOP;
  4823         }
  4824 
  4825         while (moveNext) {
  4826             String image = token.text().toString();
  4827             PlsqlTokenId tokenID = token.id();
  4828 
  4829             if ((token != null) && (!image.equals(";")) && (token.offset(tokenHierarchy) > endParse)) {
  4830                 break;
  4831             }
  4832 
  4833             if (!isLoop && image.equalsIgnoreCase("LOOP")) {
  4834                 isLoop = true;
  4835             } else if (image.equalsIgnoreCase("END")) {
  4836                 if (isLoop) {
  4837                     boolean next = getNextNonWhitespace(ts, true);
  4838                     Token<PlsqlTokenId> nextTok = ts.token();
  4839                     if (next && nextTok.text().toString().equalsIgnoreCase("LOOP")) {
  4840                         next = getNextNonWhitespace(ts, true);
  4841                         nextTok = ts.token();
  4842                         if (next && nextTok.text().toString().equalsIgnoreCase(";")) {
  4843                             ts.moveNext();
  4844                             block = new PlsqlBlock(loopBegin.offset(tokenHierarchy), ts.offset(), name.trim(), "", type);
  4845                             break;
  4846                         } else {
  4847                             break;
  4848                         }
  4849                     }
  4850                 } else {
  4851                     break;
  4852                 }
  4853             } else if (image.equalsIgnoreCase("IF")) {
  4854                 int beforeOff = token.offset(tokenHierarchy);
  4855                 List<PlsqlBlock> children = checkIfBlock(token, ts, lstChild);
  4856                 if (children == null || children.isEmpty()) {//If inner check seems to have failed need to continue this one
  4857 
  4858                     ts.move(beforeOff);
  4859                     moveNext = ts.moveNext();
  4860                 } else {
  4861                     for (int i = 0; i < children.size(); i++) {
  4862                         PlsqlBlock child = children.get(i);
  4863                         if (checkExisting(child, lstChild) == false) {
  4864                             lstChild.add(child);
  4865                         }
  4866                     }
  4867                 }
  4868             } else if (image.equalsIgnoreCase("CASE")) {
  4869                 int beforeOff = token.offset(tokenHierarchy);
  4870                 List<PlsqlBlock> children = checkCaseBlock(token, ts, lstChild, false);
  4871                 if (children == null || children.isEmpty()) {//If inner check seems to have failed need to continue this one
  4872 
  4873                     ts.move(beforeOff);
  4874                     moveNext = ts.moveNext();
  4875                 } else {
  4876                     for (int i = 0; i < children.size(); i++) {
  4877                         PlsqlBlock child = children.get(i);
  4878                         if (checkExisting(child, lstChild) == false) {
  4879                             lstChild.add(child);
  4880                         }
  4881                     }
  4882                 }
  4883             } else if (image.equalsIgnoreCase("LOOP")
  4884                     || image.equalsIgnoreCase("WHILE")
  4885                     || image.equalsIgnoreCase("FOR")) {
  4886                 int beforeOff = token.offset(tokenHierarchy);
  4887                 if (!unsuccessBlocks.contains(beforeOff)) {
  4888                     PlsqlBlock child = checkLoopBlock(token, ts, lstChild);
  4889                     if (child == null) {//If inner check seems to have failed need to continue this one
  4890                         unsuccessBlocks.add(beforeOff);
  4891                         ts.move(beforeOff);
  4892                         moveNext = ts.moveNext();
  4893                     } else {
  4894                         if (checkExisting(child, lstChild) == false) {
  4895                             lstChild.add(child);
  4896                         }
  4897                     }
  4898                 }
  4899             } else if (image.equalsIgnoreCase("TABLE")
  4900                     || image.equalsIgnoreCase("INDEX")
  4901                     || image.equalsIgnoreCase("SELECT")
  4902                     || image.equalsIgnoreCase("UPDATE")
  4903                     || image.equalsIgnoreCase("DELETE")
  4904                     || image.equalsIgnoreCase("INSERT")
  4905                     || image.equalsIgnoreCase("MERGE")
  4906                     || image.equalsIgnoreCase("DROP")
  4907                     || image.equalsIgnoreCase("SEQUENCE")) {
  4908                 if (!isNotBlockStart(token, ts)) {
  4909                     int offset = token.offset(tokenHierarchy);
  4910                     PlsqlBlock child = checkStatementBlock(token, ts, parentBlocks);
  4911                     if (child == null) {//If inner check seems to have failed need to continue this one
  4912 
  4913                         ts.move(offset);
  4914                         ts.moveNext();
  4915                     } else {
  4916                         if (checkExisting(child, lstChild) == false) {
  4917                             lstChild.add(child);
  4918                         }
  4919                     }
  4920                 }
  4921             } else if (image.equalsIgnoreCase("PROCEDURE")
  4922                     || image.equalsIgnoreCase("FUNCTION")
  4923                     || image.equalsIgnoreCase("CREATE")) {
  4924                 break;
  4925             } else if (tokenID == PlsqlTokenId.LINE_COMMENT) {
  4926                 //only single comment line
  4927                 if (image.toUpperCase(Locale.ENGLISH).contains("<FOLD>")) {
  4928                     customStartToken = token;
  4929                 } else if (image.toUpperCase(Locale.ENGLISH).contains("<END-FOLD>")) {
  4930                     if (customStartToken != null) {
  4931                         if (ts.moveNext()) {
  4932                             token = ts.token();
  4933                             PlsqlBlock custom = new PlsqlBlock(customStartToken.offset(tokenHierarchy),
  4934                                     token.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
  4935                             customFoldBlocks.add(custom);
  4936                         }
  4937 
  4938                         customStartToken = null;
  4939                     }
  4940                 } else {
  4941                     PlsqlBlock child = checkComment(token, ts);
  4942                     if (child != null) {
  4943                         if (checkExisting(child, lstChild) == false) {
  4944                             lstChild.add(child);
  4945                         }
  4946                     }
  4947                 }
  4948             } else if (tokenID == PlsqlTokenId.BLOCK_COMMENT) {
  4949                 int start = token.offset(tokenHierarchy);
  4950                 PlsqlBlock child = new PlsqlBlock(start,
  4951                         start + token.length(), "BLOCK COMMENT", "", PlsqlBlockType.COMMENT);
  4952                 if (child != null) {
  4953                     if (checkExisting(child, lstChild) == false) {
  4954                         lstChild.add(child);
  4955                     }
  4956                 }
  4957             } else if (!isLoop) {
  4958                 name = name + image;
  4959             }
  4960 
  4961             moveNext = ts.moveNext();
  4962             token = ts.token();
  4963         }
  4964 
  4965         if (block != null) {
  4966             //add children
  4967             addChildren(block, lstChild, parentBlocks);
  4968         }
  4969 
  4970         return block;
  4971     }
  4972 
  4973     /**
  4974      * Method that will add the given child blocks to the block and remove from parent blocks if existing there
  4975      *
  4976      * @param block
  4977      * @param lstChild
  4978      * @param parentBlocks
  4979      */
  4980     private void addChildren(PlsqlBlock block, List<PlsqlBlock> lstChild, List<PlsqlBlock> parentBlocks) {
  4981         int size = lstChild.size();
  4982         for (int i = 0; i < size; i++) {
  4983             PlsqlBlock child = lstChild.get(i);
  4984             block.addChild(child);
  4985             removeFromParent(child, parentBlocks);
  4986         }
  4987     }
  4988 
  4989     public int getStartParse() {
  4990         return startParse;
  4991     }
  4992 
  4993     public int getEndParse() {
  4994         return endParse;
  4995     }
  4996 
  4997     public int getChangedLength() {
  4998         return changedLength;
  4999     }
  5000 }