EADS-3749 encountering issues with the displaying of code in Developer Studio when code folding is enabled
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4 * Copyright 2011 Oracle and/or its affiliates. All rights reserved.
6 * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
7 * Other names may be trademarks of their respective owners.
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]"
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.
40 * Portions Copyrighted 2011 Sun Microsystems, Inc.
42 package org.netbeans.modules.plsql.lexer;
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;
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;
69 * Class that will maintain code blocks of the file.
73 public class PlsqlBlockFactory extends Observable implements DocumentListener {
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);
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>();
110 * Method used to reparse the whole document
114 public void reParse(Document doc) {
121 docStartOffset = doc.getStartPosition().getOffset();
122 docEndOffset = doc.getEndPosition().getOffset();
123 startParse = docStartOffset;
124 endParse = docEndOffset - 1;
126 //clean block hierarchy
127 blockHierarchy.clear();
128 customFoldBlocks.clear();
133 * Return new blocks that were recognized by the latest change
137 public List<PlsqlBlock> getNewBlocks() {
142 * Return the blocks who's offsets have changed
146 public List<PlsqlBlock> getChangedBlocks() {
147 return changedBlocks;
151 * Return the custom fold blocks that are there
155 public List<PlsqlBlock> getCustomFolds() {
156 return customFoldBlocks;
160 * Return block hierarchy
164 public List<PlsqlBlock> getBlockHierarchy() {
165 return blockHierarchy;
169 * Method that will return the blocks that are removed
173 public List<PlsqlBlock> getRemovedBlocks() {
178 * Check whether there are childrean of this fold here, if so add them
181 * @param immediateBlockHier
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);
191 removeBlock(block, immediateBlockHier);
200 * Method that will look for custom start or end token based on the given type
202 * @param customEndToken
204 * @param immediateBlockHier
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;
212 if (type.equals("START")) {
213 //We have to find the end fold token now
214 ts.move(found.offset(tokenHierarchy));
216 while (ts.moveNext()) {
218 String image = token.text().toString();
219 PlsqlTokenId tokenID = token.id();
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
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();
233 PlsqlBlock custom = new PlsqlBlock(found.offset(tokenHierarchy),
234 token.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
235 customFoldBlocks.add(custom);
238 //since we have found the other tag return
244 ts.move(found.offset(tokenHierarchy));
245 //We have to find the start fold token now
246 while (ts.movePrevious()) {
248 String image = token.text().toString();
249 PlsqlTokenId tokenID = token.id();
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)) {
256 int index = name.toUpperCase(Locale.ENGLISH).indexOf("<FOLD>");
257 name = name.substring(index + 7).trim();
258 ts.move(found.offset(tokenHierarchy));
262 PlsqlBlock custom = new PlsqlBlock(token.offset(tokenHierarchy),
263 found.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
264 customFoldBlocks.add(custom);
267 //since we have found the other tag return
269 } else if (image.toUpperCase(Locale.ENGLISH).contains("<END-FOLD>")) {
270 //We have come to another end
279 * Method that will check for a Java Source block
283 * @param immediateBlockHier
286 private PlsqlBlock checkJavaSource(Token<PlsqlTokenId> tempToken, TokenSequence<PlsqlTokenId> ts) {
287 Token<PlsqlTokenId> begin = tempToken;
288 Token<PlsqlTokenId> tmp = tempToken;
289 PlsqlBlock block = null;
291 //Check whether the beginning is in a SQL Plus command
292 if (sqlPlusLine(ts)) {
296 while (ts.moveNext()) {
298 String image = tmp.text().toString();
299 PlsqlTokenId tokenID = tmp.id();
301 if ((!image.equals("/")) && (tmp.offset(tokenHierarchy) > endParse)) {
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)) {
313 block = new PlsqlBlock(begin.offset(tokenHierarchy), offset, "", "", PlsqlBlockType.JAVA_SOURCE);
314 checkPrefix(begin.offset(tokenHierarchy), ts, block);
324 * Check whether this current token is the only token in this line
330 private boolean checkForOnlyChar(TokenSequence<PlsqlTokenId> ts, int offset) {
331 boolean isStartOk = true;
332 boolean isEndOk = true;
334 Token<PlsqlTokenId> token = null;
335 while (ts.movePrevious()) {
337 if (token.id() == PlsqlTokenId.WHITESPACE) {
338 if (token.toString().contains("\n")) {
341 } else if (token.id() == PlsqlTokenId.JAVA_SOUCE) {
350 ts.moveNext(); //current token
351 while (ts.moveNext()) {
353 if (token.id() == PlsqlTokenId.WHITESPACE) {
354 if (token.toString().contains("\n")) {
366 if (isStartOk && isEndOk) {
374 * Method that will check for statement blocks other than the CURSOR and VIEW
378 * @param immediateBlockHier
381 private PlsqlBlock checkStatementBlock(Token<PlsqlTokenId> current, TokenSequence<PlsqlTokenId> ts, List<PlsqlBlock> parentBlocks) {
382 List<PlsqlBlock> lstChild = new ArrayList<PlsqlBlock>();
383 PlsqlBlock block = null;
385 //Check whether the beginning is in a SQL Plus command
386 if (sqlPlusLine(ts)) {
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();
398 String image = token.text().toString();
399 PlsqlTokenId tokenID = token.id();
401 if ((token != null) && (!image.equals(";")) && (!image.equals("/")) && (token.offset(tokenHierarchy) > endParse)) {
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);
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")) {
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
425 moveNext = ts.moveNext();
427 for (int i = 0; i < children.size(); i++) {
428 PlsqlBlock child = (PlsqlBlock) children.get(i);
429 if (checkExisting(child, lstChild) == false) {
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) {
442 PlsqlBlock custom = new PlsqlBlock(customStartToken.offset(tokenHierarchy),
443 token.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
444 customFoldBlocks.add(custom);
446 customStartToken = null;
449 PlsqlBlock child = checkComment(token, ts);
451 if (checkExisting(child, lstChild) == false) {
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);
461 if (checkExisting(child, lstChild) == false) {
465 } else if (tokenID == PlsqlTokenId.WHITESPACE && image.contains("\n")) {
467 } else if (getName) {
471 moveNext = ts.moveNext();
477 addChildren(block, lstChild, parentBlocks);
484 * Check whether the given token offset is included in any existing block
487 * @param immediateBlockHier
491 private boolean isTokenOk(Token<PlsqlTokenId> token, List<PlsqlBlock> immediateBlockHier, PlsqlBlock parent) {
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)) {
502 if (isOk && parent != null) {
503 if (!((parent.getStartOffset() <= offset) && (parent.getEndOffset() >= offset))) {
511 * Method that will look for trigger blocks
515 * @param parentBlocks
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>();
524 String triggerName = "";
525 boolean moveNext = false;
527 //Check whether the beginning is in a SQL Plus command
528 if (sqlPlusLine(ts)) {
532 //Get procedure/function name which is the next non whitespace token
533 moveNext = getNextNonWhitespace(ts, true);
535 if (moveNext == false) {
539 triggerName = tmp.text().toString();
540 triggerName = checkForOtherSchema(ts, triggerName);
542 if (triggerName.indexOf('&') != -1) {
545 triggerName = getDefine(triggerName);
546 Token<PlsqlTokenId> customStartToken = null;
549 String image = tmp.text().toString();
550 PlsqlTokenId tokenID = tmp.id();
552 if ((tmp != null) && (!image.equals(";")) && (tmp.offset(tokenHierarchy) > endParse)) {
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")) {
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);
578 if ((prevText.equalsIgnoreCase(alias)
579 && previousNWS.text().toString().equalsIgnoreCase("END"))
580 || prevText.equalsIgnoreCase("END")) {
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);
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
609 if (checkExisting(child, lstChild) == false) {
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
621 if (checkExisting(child, lstChild) == false) {
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
634 if (checkExisting(child, lstChild) == false) {
638 } else if (image.equalsIgnoreCase("CREATE")
639 || image.equalsIgnoreCase("/")
640 || image.equalsIgnoreCase("PACKAGE")) {
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();
653 PlsqlBlock custom = new PlsqlBlock(customStartToken.offset(tokenHierarchy),
654 tmp.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
655 customFoldBlocks.add(custom);
657 customStartToken = null;
660 PlsqlBlock child = checkComment(tmp, ts);
662 if (checkExisting(child, lstChild) == false) {
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);
672 if (checkExisting(child, lstChild) == false) {
678 moveNext = ts.moveNext();
684 addChildren(block, lstChild, parentBlocks);
691 * Method that will check the prefix of the given block and change the block values accordingly
697 private void checkPrefix(int startOffset, TokenSequence<PlsqlTokenId> ts, PlsqlBlock block) {
699 int offset = ts.offset();
700 ts.move(startOffset);
702 Token<PlsqlTokenId> token = ts.token();
703 int beginOffset = startOffset;
705 while (ts.movePrevious()) {
707 String image = token.text().toString();
709 if (image.contains("\n") || (token.id() != PlsqlTokenId.KEYWORD && token.id() != PlsqlTokenId.WHITESPACE)) {
713 prefix = token.text().toString() + prefix;
714 if (token.id() != PlsqlTokenId.WHITESPACE) {
715 beginOffset = ts.offset();
721 block.setStartOffset(beginOffset);
722 block.setPrefix(prefix);
726 * Check whether there is a block existing with the given offset as the start offset
728 * @param blockHierarchy
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) {
742 if (isBlockStartExisting(tmp.getChildBlocks(), offset)) {
752 private boolean isEqual(PlsqlBlock parent, PlsqlBlock block) {
753 if ((parent == null) || (block == null)) {
756 if ((parent.getStartOffset() == block.getStartOffset())
757 || (parent.getEndOffset() == block.getEndOffset())
758 || (parent.getName().equalsIgnoreCase(block.getName()))) {
764 private String readLine(TokenSequence<PlsqlTokenId> ts, Token<PlsqlTokenId> token) {
765 String line = token.toString();
766 while (ts.moveNext()) {
768 if (token.id() == PlsqlTokenId.WHITESPACE && token.text().toString().contains("\n")) {
773 line = line + token.toString();
780 * Method that will remove the begin block of this declare block if there
782 * @param declareBlock
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);
797 * Change offsets of the blocks below the area
803 private void changeOffSet(List<PlsqlBlock> blockHier, int offset, int length, boolean add, boolean adjust) {
804 int count = blockHier.size();
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);
815 if (start + length < 0) {
816 temp.setStartOffset(0);
818 temp.setStartOffset(start + length);
819 if (startParse == start && adjust) //changing offsets of toBeRemoved
821 startParse = start + length;
825 if (temp.getPreviousEnd() == -1) {
826 temp.setPreviousEnd(end);
828 if (end + length < 0) {
829 temp.setEndOffset(0);
831 temp.setEndOffset(end + length);
832 if (endParse == end && adjust) //changing offsets of toBeRemoved
834 endParse = end + length;
839 addToChangedBlocks(temp);
841 changeOffSet(temp.getChildBlocks(), offset, length, add, adjust);
842 } else if (temp.getEndOffset() >= offset) {
843 if (temp.getPreviousEnd() == -1) {
844 temp.setPreviousEnd(end);
846 if (end + length < 0) {
847 temp.setEndOffset(0);
849 temp.setEndOffset(end + length);
850 if (endParse == end && adjust) //changing offsets of toBeRemoved
852 endParse = end + length;
857 addToChangedBlocks(temp);
859 changeOffSet(temp.getChildBlocks(), offset, length, add, adjust);
865 * Method that will check whether there are DEFINE statements in the affected area
871 private void checkAffected(Document doc, int startOffset, int endOffset) throws BadLocationException {
872 int length = endOffset - startOffset;
873 String changedText = doc.getText(startOffset, length);
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;
884 * Method that will make CURSOR blocks
888 * @param parentBlocks
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;
899 //Check whether the beginning is in a SQL Plus command
900 if (sqlPlusLine(ts)) {
904 //Get next token which is the name
905 int offset = ts.offset();
906 moveNext = getNextNonWhitespace(ts, false);
908 if (moveNext == false) {
913 String cursorName = tmp.text().toString();
915 if (cursorName.indexOf('&') != -1) {
918 cursorName = getDefine(cursorName);
920 //Next token has to be IS or ( if not leave
921 moveNext = getNextNonWhitespace(ts, false);
923 boolean isFound = false;
924 boolean parameter = false;
925 Token<PlsqlTokenId> customStartToken = null;
928 PlsqlTokenId tokenID = tmp.id();
929 String image = tmp.text().toString();
931 if ((tmp != null) && (!image.equals(";")) && (tmp.offset(tokenHierarchy) > endParse)) {
935 //When we have come up to ';' stop
936 if ((tokenID == PlsqlTokenId.OPERATOR) && (image.equals(";"))) {
948 } else if ((tokenID == PlsqlTokenId.LPAREN) && (!isFound)) {
950 } else if ((tokenID == PlsqlTokenId.RPAREN) && (!isFound)) {
952 } else if (tokenID == PlsqlTokenId.KEYWORD) {
953 if (image.equalsIgnoreCase("IS")) {
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
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
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();
987 PlsqlBlock custom = new PlsqlBlock(customStartToken.offset(tokenHierarchy),
988 tmp.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
989 customFoldBlocks.add(custom);
991 customStartToken = null;
994 PlsqlBlock child = checkComment(tmp, ts);
996 if (checkExisting(child, lstChild) == false) {
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);
1012 moveNext = ts.moveNext();
1016 if (cursorEnd != null) {
1017 block = new PlsqlBlock(cursorBegin.offset(tokenHierarchy), ts.offset(),
1018 cursorName, alias, PlsqlBlockType.CURSOR);
1021 if (block != null) {
1023 addChildren(block, lstChild, parentBlocks);
1030 * Check whether we have caught a begin of a declare block
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();
1041 while (ts.movePrevious()) {
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);
1050 //Since we have not found the block we have to avoid getting caught up in a loop
1055 } else if ((image.equals("/")) || (image.equalsIgnoreCase("BEGIN"))
1056 || ((image.equalsIgnoreCase("END")) && (tokenPre.text().toString().equals(";")))) {
1060 if ((token.id() != PlsqlTokenId.WHITESPACE)
1061 && (token.id() != PlsqlTokenId.LINE_COMMENT)
1062 && (token.id() != PlsqlTokenId.BLOCK_COMMENT)) {
1073 * Check whether the given block is already there in block hierarchy
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())) {
1096 * Clear internal variables used in a parse
1098 private void clear() {
1099 isDefineChanged = false;
1103 newBlocks = new ArrayList<PlsqlBlock>();
1104 toBeRemoved = new ArrayList<PlsqlBlock>();
1105 changedBlocks = new ArrayList<PlsqlBlock>();
1106 unsuccessBlocks = new HashSet<Integer>();
1107 resetPreviousValues(blockHierarchy);
1110 private void resetPreviousValues(List<PlsqlBlock> blockList) {
1111 for (PlsqlBlock block : blockList) {
1112 block.setPreviousStart(-1);
1113 block.setPreviousEnd(-1);
1114 resetPreviousValues(block.getChildBlocks());
1118 public void beforeCaseChange() {
1119 caseChangeInProgress = true;
1122 public void afterCaseChange() {
1123 caseChangeInProgress = false;
1126 public void beforeSave(Document document) {
1127 saveInProgress = true;
1128 synchronized (updateLock) {
1129 updateEvents.clear();
1133 public boolean isSaveInProgress() {
1134 return saveInProgress || caseChangeInProgress;
1137 public synchronized void afterSave(Document document) {
1139 //initHierarchy(document);
1142 notifyObservers(document);
1144 saveInProgress = false;
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;
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);
1166 toBeRemoved.add(block);
1171 //If this block is there in changed blocks remove it
1172 removeBlock(changedBlocks, block);
1173 if (startParse > block.getStartOffset()) {
1174 startParse = block.getStartOffset();
1176 if (endParse < block.getEndOffset()) {
1177 endParse = block.getEndOffset();
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()) {
1194 changedBlocks.add(temp);
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()) {
1205 if (token.id() == PlsqlTokenId.DOT) {
1206 if (ts.moveNext()) {
1207 return ts.token().toString();
1210 return ts.token().toString();
1216 //Reset the original location
1223 private static class EventProperties {
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;
1231 public EventProperties(PlsqlBlockFactory blockFactory) {
1232 this.blockFactory = blockFactory;
1236 private static class UpdateBlocksThread implements Runnable {
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>();
1246 while (event != null && event.document.equals(doc)) {
1247 updateEvents.removeFirst();
1250 if (updateEvents.size() > 0) {
1251 event = updateEvents.getFirst();
1255 docList.get(0).blockFactory.doUpdate(doc, docList);
1261 private synchronized void doUpdate(final Document document, final List<EventProperties> docList) {
1262 LOG.log(Level.FINE, "doUpdate", new Object[]{document, docList});
1264 //make sure that the updates are run in the Swing thread
1265 SwingUtilities.invokeLater(new Runnable() {
1269 updateBlocks(document, docList);
1271 notifyObservers(document);
1277 private void addUpdateEvent(DocumentEvent e, DocumentEvent.EventType mode) {
1278 synchronized (updateLock) {
1279 if (updateBlocksTask == null) {
1280 updateBlocksTask = RP.create(new UpdateBlocksThread());
1282 updateBlocksTask.schedule(DEFAULT_WAIT_TIME);
1283 addNewEvent(e, mode);
1288 * Event fired on insert
1293 public void insertUpdate(DocumentEvent e) {
1294 LOG.log(Level.FINER, "insertUpdate", e);
1295 if (isSaveInProgress()) {
1298 addUpdateEvent(e, DocumentEvent.EventType.INSERT);
1302 * Event fired in remove
1307 public void removeUpdate(DocumentEvent e) {
1308 LOG.log(Level.FINER, "removeUpdate", e);
1309 if (isSaveInProgress()) {
1312 addUpdateEvent(e, DocumentEvent.EventType.REMOVE);
1316 * triggered when opening a different document
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
1327 * Update block hierarchy on a document event
1332 public synchronized void initHierarchy(Document doc) {
1339 docStartOffset = doc.getStartPosition().getOffset();
1340 docEndOffset = doc.getEndPosition().getOffset();
1341 Object obj = doc.getProperty("Listener");
1343 if ((obj == null) || (!obj.equals("YES"))) {
1344 startParse = docStartOffset;
1345 endParse = docEndOffset - 1;
1347 //clean block hierarchy
1348 blockHierarchy.clear();
1349 customFoldBlocks.clear();
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);
1359 * Method that will return the block within the start & end parse
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) {
1386 * Get the line offset of the beginning of this block end line
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());
1397 return parent.getEndOffset();
1399 int preEnd = parent.getEndOffset();
1401 //go to the previous line break
1403 boolean movePrevious = ts.movePrevious();
1404 Token<PlsqlTokenId> tokenPre = ts.token();
1406 while (movePrevious) {
1407 if (tokenPre.text().toString().contains("\n")) {
1408 preEnd = tokenPre.offset(tokenHier);
1411 movePrevious = ts.movePrevious();
1412 tokenPre = ts.token();
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;
1424 while (ts.movePrevious()) {
1427 if (loopCount == 0 && token.id() == PlsqlTokenId.DOT) {
1428 isNotBlockStart = true;
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;
1439 } else if (token.text().toString().equalsIgnoreCase("TYPE")) {
1440 isNotBlockStart = true;
1448 if (token.text().toString().equalsIgnoreCase("TYPE")
1449 || token.text().toString().equalsIgnoreCase("GRANT")) {
1450 isNotBlockStart = true;
1455 return isNotBlockStart;
1459 * Get the line offset of the second line of this block start
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());
1470 return parent.getStartOffset();
1472 int preStart = parent.getStartOffset();
1474 boolean moveNext = ts.moveNext();
1475 Token<PlsqlTokenId> tokenNext = ts.token();
1478 if (tokenNext.text().toString().contains("\n")) {
1479 preStart = tokenNext.offset(tokenHier);
1482 moveNext = ts.moveNext();
1483 tokenNext = ts.token();
1490 * If any defines are changed change the affected names
1492 private void parseAliases() {
1493 if (!isDefineChanged) {
1497 int size = blockHierarchy.size();
1498 for (int i = 0; i < size; i++) {
1499 PlsqlBlock block = blockHierarchy.get(i);
1500 evaluateBlock(block);
1504 public boolean isAliasesChanged() {
1505 return isDefineChanged;
1509 * Method that will evaluate the given block and decide whether the name has to be changed
1513 private void evaluateBlock(PlsqlBlock block) {
1514 String alias = block.getAlias();
1515 if (!alias.equals("")) {
1516 block.setName(getDefine(alias));
1519 int childCount = block.getChildBlocks().size();
1520 for (int i = 0; i < childCount; i++) {
1521 PlsqlBlock child = block.getChildBlocks().get(i);
1522 evaluateBlock(child);
1527 * Remove given block from the hierarchy
1532 private boolean removeBlock(List<PlsqlBlock> blockHier, PlsqlBlock block) {
1533 int count = blockHier.size();
1534 boolean isFound = false;
1536 for (int i = 0; i < count; i++) {
1537 PlsqlBlock temp = blockHier.get(i);
1539 if ((temp.getStartOffset() == block.getStartOffset())
1540 && (temp.getEndOffset() == block.getEndOffset())) {
1541 blockHier.remove(temp);
1545 if ((temp.getStartOffset() < block.getStartOffset())
1546 && (temp.getEndOffset() > block.getEndOffset())) { //block is a child
1548 if (removeBlock(temp.getChildBlocks(), block)) {
1560 * Add child blocks enclosed by the change area to the remove list (do not update the parse area here, done only in
1564 * @param plsqlBlocks
1565 * @param toBeRemoved
1566 * @param startOffset
1570 private void removeEnclosedBlocks(Document doc, List<PlsqlBlock> plsqlBlocks, int startOffset, int endOffset) {
1571 int count = plsqlBlocks.size();
1573 for (int i = count - 1; i >= 0; i--) {
1574 PlsqlBlock block = plsqlBlocks.get(i);
1576 if ((block.getEndOffset() <= endOffset)
1577 && (block.getStartOffset() >= startOffset)) { //blocks which are enclosed by the affected area
1578 removeBlock(block, plsqlBlocks);
1580 removeEnclosedBlocks(doc, block.getChildBlocks(), startOffset, endOffset);
1586 * Add child blocks affected by the change area to the remove list and update the parse area
1589 * @param plsqlBlocks
1590 * @param toBeRemoved
1591 * @param startOffset
1595 private boolean removeBlocksWithin(Document doc, List<PlsqlBlock> plsqlBlocks, int startOffset, int endOffset) {
1596 boolean blockIsFound = false;
1598 int count = plsqlBlocks.size();
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;
1624 boolean isFound = removeBlocksWithin(doc, block.getChildBlocks(), startOffset, endOffset);
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;
1651 return blockIsFound;
1655 * Remove custom fold blocks that are there within the parse area
1657 * @param customFoldBlocks
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);
1680 * If given block exists in the hier delete
1683 * @param parentBlocks
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);
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()) {
1703 if (token.id() == PlsqlTokenId.WHITESPACE && token.toString().contains("\n")) {
1704 if (tokenPre != null && tokenPre.id() == PlsqlTokenId.SQL_PLUS) {
1710 if (token.id() != PlsqlTokenId.WHITESPACE) {
1715 if (tokenPre != null && tokenPre.id() == PlsqlTokenId.SQL_PLUS) {
1725 * Update block hierarchy based on the document event and the action
1727 private synchronized void updateBlocks(Document doc, List<EventProperties> docList) {
1728 LOG.log(Level.FINE, "updateBlocks", new Object[]{doc, docList});
1731 ((AbstractDocument) doc).readLock();
1733 docStartOffset = doc.getStartPosition().getOffset();
1734 docEndOffset = doc.getEndPosition().getOffset();
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;
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;
1751 //get the affected area
1752 int startOffset = offset;
1753 int endOffset = startOffset + length;
1754 if (action == DocumentEvent.EventType.REMOVE) {
1755 endOffset = startOffset;
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;
1774 //adjust offsets according to the change
1775 if (startParse > startOffset) {
1776 startParse = startOffset;
1778 if (endParse < endOffset) {
1779 endParse = endOffset;
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
1787 startParse = docStartOffset;
1788 endParse = docEndOffset - 1;
1790 if (startParse == startOffset) {//if start offset already adjusted no need to do again
1792 PlsqlBlock block = null;
1793 block = getImmediateBefore(blockHierarchy, block, startOffset);
1794 PlsqlBlock parent = getParentBlock(blockHierarchy, startParse, endParse);
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;
1803 removeBlock(block, parent.getChildBlocks());
1806 if (block != null) {
1807 removeBlock(block, blockHierarchy);
1808 } else if (parent != null) { // we are not going to remove the parent here
1810 int newStart = getSecondLineOfBlock(doc, parent);
1811 if (newStart < startParse) {
1812 startParse = newStart;
1815 startParse = docStartOffset;
1820 if (endParse == endOffset) {//if end offset already adjusted no need to do again
1822 PlsqlBlock block = null;
1823 block = getImmediateAfter(blockHierarchy, block, endOffset);
1824 PlsqlBlock parent = getParentBlock(blockHierarchy, startParse, endParse);
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) {
1833 removeBlock(block, parent.getChildBlocks());
1836 if (block != null) {
1837 removeBlock(block, blockHierarchy);
1838 } else if (parent != null) { // we are not going to remove the parent here
1840 int newEnd = getPreLineOfBlockEnd(doc, parent);
1841 if (newEnd > endParse) {
1845 endParse = docEndOffset - 1;
1850 //Remove custom fold blocks that are there within the parse area
1851 removeCustomBlocks(customFoldBlocks, startParse, endParse);
1853 //When files are deleted and written after opening once following can happen
1854 if ((endParse < 0) || (endParse > docEndOffset - 1)) {
1855 endParse = docEndOffset - 1;
1858 if (startParse < 0) {
1859 startParse = docStartOffset;
1862 if (startParse > docEndOffset - 1) {
1863 startParse = docEndOffset - 1;
1866 } else { //UPDATE pass again
1868 startParse = docStartOffset;
1869 endParse = docEndOffset - 1;
1871 //clean block hierarchy
1872 blockHierarchy.clear();
1873 customFoldBlocks.clear();
1875 //we are going for a reparse, remove all the events for this doc
1881 if (startParse >= endParse) //Can happen in removes
1886 //Check whether defines are changed from the affected area
1887 checkAffected(doc, startParse, endParse);
1889 if (isDefineChanged) {
1893 //pass affected section again
1894 generateBlocks(doc);
1896 } catch (Exception e) {
1897 ErrorManager.getDefault().notify(e);
1899 ((AbstractDocument) doc).readUnlock();
1904 * Get block which is immediately before the given offset
1911 private PlsqlBlock getImmediateBefore(List blocks, PlsqlBlock block, int offset) {
1912 int count = blocks.size();
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) {
1922 if ((temp.getEndOffset() < offset)
1923 && ((block == null) || (block.getEndOffset() < temp.getEndOffset()))) {
1932 * Get fold which is immediately after the given offset
1939 private PlsqlBlock getImmediateAfter(List<PlsqlBlock> blocks, PlsqlBlock block, int offset) {
1940 int count = blocks.size();
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) {
1950 if ((temp.getStartOffset() > offset)
1951 && ((block == null) || (block.getStartOffset() > temp.getStartOffset()))) {
1960 * Method that will generate blocks of the given offset range
1962 * @param startOffset
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());
1974 PlsqlBlock parent = getParentBlock(blockHierarchy, startParse, endParse);
1975 if (parent != null) {
1976 immediateBlockHier = parent.getChildBlocks();
1978 immediateBlockHier = blockHierarchy;
1981 LOG.log(Level.FINE, "generateBlocks, doc.getLength()={0}, ts.tokenCount()={1}",
1982 new Object[]{doc.getLength(), ts.tokenCount()});
1984 ts.move(startParse);
1985 Token<PlsqlTokenId> tempToken;
1986 Token<PlsqlTokenId> customStartToken = null;
1987 Token<PlsqlTokenId> customEndToken = null;
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();
1995 LOG.log(Level.FINE, "tempToken.id()={0}, tempToken.text()={1}",
1996 new Object[]{tempToken.id(), tempToken.text()});
1998 //Check end offset and break (exception for colun which will mark end of some blocks)
1999 if ((!image.equals(";")) && (tempToken.offset(tokenHierarchy) > endParse)) {
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
2012 checkAndAddNew(block, parent, immediateBlockHier);
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
2022 checkAndAddNew(block, parent, immediateBlockHier);
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
2032 checkAndAddNew(block, parent, immediateBlockHier);
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
2042 checkAndAddNew(block, parent, immediateBlockHier);
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
2052 checkAndAddNew(block, parent, immediateBlockHier);
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
2062 checkAndAddNew(block, parent, immediateBlockHier);
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
2072 checkAndAddNew(block, parent, immediateBlockHier);
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);
2079 } else if (image.equalsIgnoreCase("BEGIN")) {
2080 if (!isDeclare(ts, immediateBlockHier)) {//We need to check whether the declare is isolated by a CURSOR block
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
2089 checkAndAddNew(block, parent, immediateBlockHier);
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
2102 for (int i = 0; i < children.size(); i++) {
2103 PlsqlBlock child = (PlsqlBlock) children.get(i);
2104 checkAndAddNew(child, parent, immediateBlockHier);
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);
2116 if (children == null || children.isEmpty()) {//If inner check seems to have failed need to continue this one
2121 for (int i = 0; i < children.size(); i++) {
2122 PlsqlBlock child = (PlsqlBlock) children.get(i);
2123 checkAndAddNew(child, parent, immediateBlockHier);
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
2137 for (int i = 0; i < children.size(); i++) {
2138 PlsqlBlock child = (PlsqlBlock) children.get(i);
2139 checkAndAddNew(child, parent, immediateBlockHier);
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);
2155 checkAndAddNew(child, parent, immediateBlockHier);
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
2176 checkAndAddNew(child, parent, immediateBlockHier);
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
2189 checkAndAddNew(block, parent, immediateBlockHier);
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);
2206 customStartToken = null;
2208 customEndToken = tempToken;
2211 PlsqlBlock block = checkComment(tempToken, ts);
2212 if (block != null) {
2213 checkAndAddNew(block, parent, immediateBlockHier);
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);
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);
2239 } //we have come to the end now, check whether we have unmatched custom tokens
2241 if (customEndToken != null) {
2242 checkCustom(customEndToken, ts, immediateBlockHier, parent, "END");
2243 } else if (customStartToken != null) {
2244 checkCustom(customStartToken, ts, immediateBlockHier, parent, "START");
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);
2261 * Check whether current ';' is the end of a function/procedure/view/package
2266 private PlsqlBlock checkEnd(Token<PlsqlTokenId> endToken, TokenSequence<PlsqlTokenId> ts) {
2267 Token<PlsqlTokenId> end = endToken; //means ';' here
2269 Token<PlsqlTokenId> begin = null;
2270 String methodName = "";
2271 int type = -1; //1 for FUNCTION/PROCEDURE/PACKAGE
2273 boolean moveBack = false;
2274 PlsqlBlock block = null;
2275 moveBack = getPreviousNonWhitespace(ts, true);
2276 Token<PlsqlTokenId> tmp = ts.token();
2278 if (moveBack == false) {
2279 ts.move(end.offset(tokenHierarchy));
2284 //If this is a function/procedure end take the name
2285 if ((tmp.id() == PlsqlTokenId.KEYWORD) && (tmp.text().toString().equalsIgnoreCase("END"))) {
2287 } else if (tmp.id() == PlsqlTokenId.IDENTIFIER || tmp.id() == PlsqlTokenId.KEYWORD) {
2288 methodName = tmp.text().toString();
2290 //Check whether this is an 'end <ide>;'
2291 moveBack = getPreviousNonWhitespace(ts, true);
2294 if (moveBack == false) {
2295 ts.move(end.offset(tokenHierarchy));
2300 if ((tmp.id() == PlsqlTokenId.KEYWORD)
2301 && (tmp.text().toString().equalsIgnoreCase("END"))) {
2308 begin = checkMethodBegin(ts, methodName);
2309 } else if (type == 0) {
2310 begin = checkMethodBegin(ts);
2312 Token<PlsqlTokenId> nameToken = begin;
2314 if (getNextNonWhitespace(ts, true)) {
2315 nameToken = ts.token();
2316 if (!nameToken.text().toString().equalsIgnoreCase("BODY")) {
2317 methodName = nameToken.text().toString();
2319 if (getNextNonWhitespace(ts, true)) {
2320 nameToken = ts.token();
2321 methodName = nameToken.text().toString();
2327 //Cold block is found
2328 if (begin != null) {
2329 String image = begin.text().toString();
2331 if (methodName.indexOf('&') != -1) {
2335 methodName = getDefine(methodName);
2337 //Get the token after the end token
2338 ts.move(end.offset(tokenHierarchy));
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
2349 int endPos = ts.offset();
2350 ts.move(begin.offset(tokenHierarchy));
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;
2360 block = new PlsqlBlock(begin.offset(tokenHierarchy), endPos,
2361 methodName, alias, foldType);
2362 checkPrefix(begin.offset(tokenHierarchy), ts, block);
2364 block = new PlsqlBlock(begin.offset(tokenHierarchy), ts.offset(),
2365 methodName, alias, PlsqlBlockType.FUNCTION_IMPL);
2366 checkPrefix(begin.offset(tokenHierarchy), ts, block);
2370 ts.move(end.offset(tokenHierarchy));
2376 * Check whether there is a function/procedure in this block
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();
2389 String image = tmp.text().toString();
2391 if (image.equalsIgnoreCase(methodName)) {
2392 //Go to previous word
2393 boolean move = getPreviousNonWhitespace(ts, true);
2394 Token<PlsqlTokenId> token = ts.token();
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) {
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"))) {
2412 } else if (token.text().toString().equalsIgnoreCase("END")) {
2417 moveBack = getPreviousNonWhitespace(ts, true);
2425 * Check whether there is a function/procedure in this block Method name is not there in the 'END;'
2430 private Token<PlsqlTokenId> checkMethodBegin(TokenSequence<PlsqlTokenId> ts) {
2431 Token<PlsqlTokenId> begin = null;
2432 boolean moveBack = ts.movePrevious();
2433 Token<PlsqlTokenId> tmp = ts.token();
2437 String image = tmp.text().toString();
2439 if (tmp.id() == PlsqlTokenId.KEYWORD) {
2440 if ((image.equalsIgnoreCase("BEGIN"))) {
2442 } else if ((image.equalsIgnoreCase("END"))) {
2443 int off = ts.offset();
2444 if (getNextNonWhitespace(ts, true)) {
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")))) {
2456 } else if ((endCount == 0) & (image.equalsIgnoreCase("PACKAGE"))) {
2458 } else if ((endCount == -1) && ((image.equalsIgnoreCase("PROCEDURE"))
2459 || (image.equalsIgnoreCase("FUNCTION")))) {
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"))) {
2470 moveBack = getPreviousNonWhitespace(ts, true);
2478 * Check whether this is a block comment, single line or multi lined comment
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("")) {
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...")) {
2504 PlsqlTokenId tokenID = tmp.id();
2506 //We have come to the end of the view declaration
2507 if (tokenID != PlsqlTokenId.LINE_COMMENT) {
2512 text = text + tmp.text().toString();
2513 desc = getCommentDescription(text);
2514 if (!desc.equals("COMMENT...")) {
2518 moveNext = getNextNonWhitespaceForComments(ts);
2523 ts.move(commentEnd.offset(tokenHierarchy));
2526 //Calculate end offset
2527 if(commentEnd.id() == PlsqlTokenId.WHITESPACE ){
2529 commentEnd = ts.token();
2531 int endOffset = commentEnd.offset(tokenHierarchy) + commentEnd.length();
2533 return new PlsqlBlock(commentBegin.offset(tokenHierarchy),
2534 endOffset, desc, "", PlsqlBlockType.COMMENT);
2538 * Method that will give the description of the comment fold
2543 private String getCommentDescription(String text) {
2544 String description = "COMMENT...";
2546 //Get first -- character from begin to end
2547 char[] textArr = text.toCharArray();
2551 //Get the start character which is not -
2553 while (textArr.length > i) {
2554 if ((textArr[i] != '-') && (textArr[i] != ' ')) {
2561 //Get end character which is -
2563 while (textArr.length > i) {
2564 if (textArr[i] == '-') {
2573 description = "-- " + text.substring(begin, end);
2580 * Check whether this is the start of a PROCEDURE/FUNCTION block
2582 * @param methodToken
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;
2595 String methodName = "";
2596 boolean moveNext = false;
2597 int beginCount = 0; //workaround to identify end of method...
2599 //Check whether the beginning is in a SQL Plus command
2600 if (sqlPlusLine(ts)) {
2604 //Get procedure/function name which is the next non whitespace token
2605 moveNext = getNextNonWhitespace(ts, true);
2607 if (moveNext == false) {
2611 methodName = tmp.text().toString();
2612 methodName = checkForOtherSchema(ts, methodName);
2614 if (methodName.indexOf('&') != -1) {
2618 methodName = getDefine(methodName);
2619 Token<PlsqlTokenId> customStartToken = null;
2620 Token<PlsqlTokenId> previous = tmp;
2622 //Check whether there is the keyword 'IS' before ';'
2624 String image = tmp.text().toString();
2625 PlsqlTokenId tokenID = tmp.id();
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
2631 //Increment colun count, if IS is not found before first break
2632 if ((tokenID == PlsqlTokenId.OPERATOR) && image.equals(";")) {
2634 boolean isEndFound = false;
2635 int offset = ts.offset();
2636 boolean preMove = false;
2637 preMove = getPreviousNonWhitespace(ts, true);
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) {
2647 preMove = getPreviousNonWhitespace(ts, true);
2648 previousNWS = ts.token();
2649 if ((previd == PlsqlTokenId.IDENTIFIER || previd == PlsqlTokenId.KEYWORD)
2650 && previousNWS.text().toString().equalsIgnoreCase("END")) {
2656 if ((colunCount == 1) && (!isFound) && (!pragmaFound) && (!isEndFound)) {
2657 //Although we were looking for impl's we have found a def here
2660 if (type == PlsqlBlockType.PROCEDURE_IMPL) {
2661 block = new PlsqlBlock(methodBegin.offset(tokenHierarchy),
2662 ts.offset(), methodName, alias, PlsqlBlockType.PROCEDURE_DEF);
2664 block = new PlsqlBlock(methodBegin.offset(tokenHierarchy),
2665 ts.offset(), methodName, alias, PlsqlBlockType.FUNCTION_DEF);
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) {
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);
2693 preMove = getPreviousNonWhitespace(ts, true);
2694 previousNWS = ts.token();
2695 if (previousNWS.text().toString().equalsIgnoreCase("END")) {
2696 if (beginCount <= 0) {
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);
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);
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
2721 moveNext = ts.moveNext();
2723 if (checkExisting(child, lstChild) == false) {
2724 lstChild.add(child);
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
2733 moveNext = ts.moveNext();
2735 if (checkExisting(child, lstChild) == false) {
2736 lstChild.add(child);
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
2745 moveNext = ts.moveNext();
2747 if (checkExisting(child, lstChild) == false) {
2748 lstChild.add(child);
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
2765 < children.size(); i++) {
2766 PlsqlBlock child = (PlsqlBlock) children.get(i);
2767 if (checkExisting(child, lstChild) == false) {
2768 lstChild.add(child);
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
2779 moveNext = ts.moveNext();
2782 < children.size(); i++) {
2783 PlsqlBlock child = (PlsqlBlock) children.get(i);
2784 if (checkExisting(child, lstChild) == false) {
2785 lstChild.add(child);
2789 } else if ((tokenID == PlsqlTokenId.KEYWORD)
2790 && ((image.equalsIgnoreCase("LOOP"))
2791 || (image.equalsIgnoreCase("WHILE"))
2792 || (image.equalsIgnoreCase("FOR")))) {
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);
2800 moveNext = ts.moveNext();
2802 if (checkExisting(child, lstChild) == false) {
2803 lstChild.add(child);
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
2825 if (checkExisting(child, lstChild) == false) {
2826 lstChild.add(child);
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>");
2839 name.substring(index + 7).trim();
2840 if (ts.moveNext()) {
2842 PlsqlBlock custom = new PlsqlBlock(customStartToken.offset(tokenHierarchy),
2843 tmp.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
2844 customFoldBlocks.add(custom);
2846 customStartToken = null;
2849 PlsqlBlock child = checkComment(tmp, ts);
2850 if ((child != null) && (checkExisting(child, lstChild) == false)) {
2851 lstChild.add(child);
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);
2861 } else if (tokenID == PlsqlTokenId.KEYWORD && (image.equalsIgnoreCase("BEGIN"))) {
2863 } else if (tokenID == PlsqlTokenId.KEYWORD && image.equalsIgnoreCase("END")) {
2864 int off = ts.offset();
2865 if (getNextNonWhitespace(ts, true)) {
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")))) {
2878 } else if (tokenID == PlsqlTokenId.KEYWORD && image.equalsIgnoreCase("PRAGMA")) {
2881 //Mark when we come to 'IS'
2882 if ((tokenID == PlsqlTokenId.KEYWORD)
2883 && ((image.equalsIgnoreCase("IS")))) {
2887 if (tokenID != PlsqlTokenId.WHITESPACE) {
2891 moveNext = ts.moveNext();
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()) {
2905 addChildren(block, lstChild, parentBlocks);
2912 * Check whether this is the start of a PACKAGE block
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>();
2930 //Check whether the beginning is in a SQL Plus command
2931 if (sqlPlusLine(ts)) {
2935 //Get package name which is the next non whitespace token in spec
2936 moveNext = getNextNonWhitespace(ts, true);
2938 if (moveNext == false) {
2942 if (tmp.text().toString().equalsIgnoreCase("BODY")) {
2943 isPackageBody = true;
2944 moveNext = getNextNonWhitespace(ts, true);
2946 if (moveNext == false) {
2951 packageName = tmp.text().toString();
2952 packageName = checkForOtherSchema(ts, packageName);
2954 if (packageName.indexOf('&') != -1) {
2955 alias = packageName;
2956 } else if (hasDefineKey(packageName)) {
2957 alias = '&' + getDefineKey(packageName);
2960 packageName = getDefine(packageName);
2961 Token<PlsqlTokenId> customStartToken = null;
2964 String image = tmp.text().toString();
2965 PlsqlTokenId tokenID = tmp.id();
2967 if ((!image.equals(";")) && (tmp.offset(tokenHierarchy) > endParse)) {
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();
2988 if ((preMove != false)
2989 && previousNWS.text().toString().equalsIgnoreCase("END")) {
2992 } else if ((tmpPre.text().toString().equalsIgnoreCase("END")) && (beginCount < 0)) {
2996 //If this is a package end create the block
2998 PlsqlBlockType type = PlsqlBlockType.PACKAGE;
3000 if (isPackageBody) {
3001 type = PlsqlBlockType.PACKAGE_BODY;
3006 block = new PlsqlBlock(packBegin.offset(tokenHierarchy),
3007 ts.offset(), packageName, alias, type);
3008 checkPrefix(packBegin.offset(tokenHierarchy), ts, block);
3011 } else if ((tokenID == PlsqlTokenId.KEYWORD)
3012 && ((image.equalsIgnoreCase("PROCEDURE"))
3013 || (image.equalsIgnoreCase("FUNCTION"))
3014 || (image.equalsIgnoreCase("CURSOR")))) {
3016 int beforeOff = tmp.offset(tokenHierarchy);
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
3025 if (checkExisting(child, lstChild) == false) {
3026 lstChild.add(child);
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
3037 if (checkExisting(child, lstChild) == false) {
3038 lstChild.add(child);
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
3049 if (checkExisting(child, lstChild) == false) {
3050 lstChild.add(child);
3058 } else if (tokenID == PlsqlTokenId.KEYWORD && (image.equalsIgnoreCase("BEGIN"))) {
3060 } else if ((tokenID == PlsqlTokenId.KEYWORD)
3061 && (image.equalsIgnoreCase("END"))) {
3062 int off = ts.offset();
3063 if (getNextNonWhitespace(ts, true)) {
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")))) {
3076 } else if ((tokenID == PlsqlTokenId.KEYWORD)
3077 && ((image.equalsIgnoreCase("CREATE")) || (image.equalsIgnoreCase("PACKAGE")))) {
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()) {
3090 PlsqlBlock custom = new PlsqlBlock(customStartToken.offset(tokenHierarchy),
3091 tmp.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
3092 customFoldBlocks.add(custom);
3094 customStartToken = null;
3097 PlsqlBlock child = checkComment(tmp, ts);
3098 if ((child != null) && (checkExisting(child, lstChild) == false)) {
3099 lstChild.add(child);
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);
3110 //Mark when we come to 'IS'
3111 if ((tokenID == PlsqlTokenId.KEYWORD)
3112 && ((image.equalsIgnoreCase("IS")) || (image.equalsIgnoreCase("AS")))) {
3117 if (tokenID != PlsqlTokenId.WHITESPACE) {//previous non whitespace token
3122 moveNext = ts.moveNext();
3126 if (block != null) {
3128 addChildren(block, lstChild, parentBlocks);
3130 //Add immediate children
3131 addImmediateChildren(block, parentBlocks);
3138 * Method that will check declare end blocks
3142 * @param parentBlocks
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;
3153 //Check whether the beginning is in a SQL Plus command
3154 if (sqlPlusLine(ts)) {
3158 moveNext = ts.moveNext();
3160 declareBegin = current;
3161 Token<PlsqlTokenId> customStartToken = null;
3164 PlsqlTokenId tokenID = token.id();
3165 String image = token.text().toString();
3167 if ((token != null) && (!image.equals(";")) && (token.offset(tokenHierarchy) > endParse)) {
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();
3177 ts.move(token.offset(tokenHierarchy));
3181 if (image.equals("/")) {
3182 if (pre.toString().equals(";")) {
3183 movePre = getPreviousNonWhitespace(ts, true);
3185 if ((movePre) && (pre.toString().equalsIgnoreCase("END"))) {
3186 ts.move(token.offset(tokenHierarchy));
3189 block = new PlsqlBlock(declareBegin.offset(tokenHierarchy),
3190 ts.offset(), "", "", PlsqlBlockType.DECLARE_END);
3191 removeChildBegin(block);
3195 //something has gone wrong '/' is a terminal
3199 if ((movePre) && (pre.toString().equalsIgnoreCase("END"))) {
3200 ts.move(token.offset(tokenHierarchy));
3203 block = new PlsqlBlock(declareBegin.offset(tokenHierarchy),
3204 ts.offset(), "", "", PlsqlBlockType.DECLARE_END);
3205 removeChildBegin(block);
3209 ts.move(token.offset(tokenHierarchy));
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
3217 moveNext = ts.moveNext();
3219 if (checkExisting(child, lstChild) == false) {
3220 lstChild.add(child);
3223 } else if (image.equalsIgnoreCase("PROCEDURE")) {
3224 if (isBeginFound) {//Can be there only before begin in a declare block
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
3234 moveNext = ts.moveNext();
3236 if (checkExisting(child, lstChild) == false) {
3237 lstChild.add(child);
3240 } else if (image.equalsIgnoreCase("FUNCTION")) {
3241 if (isBeginFound) {//Can be there only before begin in a declare block
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
3251 moveNext = ts.moveNext();
3253 if (checkExisting(child, lstChild) == false) {
3254 lstChild.add(child);
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);
3265 } else if (image.equalsIgnoreCase("BEGIN")) {
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
3272 moveNext = ts.moveNext();
3274 if (checkExisting(child, lstChild) == false) {
3275 lstChild.add(child);
3279 isBeginFound = true;
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
3287 moveNext = ts.moveNext();
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);
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
3302 moveNext = ts.moveNext();
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);
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);
3320 moveNext = ts.moveNext();
3322 if (checkExisting(child, lstChild) == false) {
3323 lstChild.add(child);
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
3344 if (checkExisting(child, lstChild) == false) {
3345 lstChild.add(child);
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()) {
3360 PlsqlBlock custom = new PlsqlBlock(customStartToken.offset(tokenHierarchy),
3361 token.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
3362 customFoldBlocks.add(custom);
3365 customStartToken = null;
3368 PlsqlBlock child = checkComment(token, ts);
3369 if (child != null) {
3370 if (checkExisting(child, lstChild) == false) {
3371 lstChild.add(child);
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);
3386 moveNext = ts.moveNext();
3390 if (block != null) {
3392 addChildren(block, lstChild, parentBlocks);
3394 if (moveNext) { //If have come to last return otherwise we will loop with begin
3395 ts.move(declareBegin.offset(tokenHierarchy));
3404 * Method that will check begin end blocks
3408 * @param parentBlocks
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;
3418 //Check whether the beginning is in a SQL Plus command
3419 if (sqlPlusLine(ts)) {
3423 moveNext = ts.moveNext();
3426 Token<PlsqlTokenId> customStartToken = null;
3429 PlsqlTokenId tokenID = token.id();
3430 String image = token.text().toString();
3432 if ((token != null) && (!image.equals(";")) && (token.offset(tokenHierarchy) > endParse)) {
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();
3442 ts.move(token.offset(tokenHierarchy));
3446 if (image.equals("/")) {
3447 if (pre.toString().equals(";")) {
3448 movePre = getPreviousNonWhitespace(ts, true);
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));
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);
3466 //something has gone wrong '/' is a terminal
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));
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);
3485 ts.move(token.offset(tokenHierarchy));
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);
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
3501 moveNext = ts.moveNext();
3503 if (checkExisting(child, lstChild) == false) {
3504 lstChild.add(child);
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
3513 moveNext = ts.moveNext();
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);
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
3528 moveNext = ts.moveNext();
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);
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);
3546 moveNext = ts.moveNext();
3548 if (checkExisting(child, lstChild) == false) {
3549 lstChild.add(child);
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
3570 if (checkExisting(child, lstChild) == false) {
3571 lstChild.add(child);
3575 } else if (image.equalsIgnoreCase("PROCEDURE")
3576 || image.equalsIgnoreCase("FUNCTION")) {
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()) {
3589 PlsqlBlock custom = new PlsqlBlock(customStartToken.offset(tokenHierarchy),
3590 token.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
3591 customFoldBlocks.add(custom);
3594 customStartToken = null;
3597 PlsqlBlock child = checkComment(token, ts);
3598 if (child != null) {
3599 if (checkExisting(child, lstChild) == false) {
3600 lstChild.add(child);
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);
3615 moveNext = ts.moveNext();
3619 if (block != null) {
3621 addChildren(block, lstChild, parentBlocks);
3628 * Method to check table & column comments
3632 * @param parentBlocks
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>();
3641 //Check whether the beginning is in a SQL Plus command
3642 if (sqlPlusLine(ts)) {
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);
3654 Token<PlsqlTokenId> customStartToken = null;
3656 //Check whether that is table/column comment
3657 if (moveNext == false) {
3661 Token<PlsqlTokenId> tmp = ts.token(); //catching ON
3663 moveNext = getNextNonWhitespace(ts, true);
3664 tmp = ts.token(); //TABLE OR VIEW
3666 if ((moveNext != false) && (tmp.id() == PlsqlTokenId.KEYWORD)) {
3667 String image = tmp.text().toString();
3668 if (image.equalsIgnoreCase("TABLE")) {
3671 moveNext = getNextNonWhitespace(ts, true);
3674 if (moveNext != false) {
3675 tableName = tmp.text().toString();
3676 tableName = checkForOtherSchema(ts, tableName);
3677 if (tableName.indexOf('&') != -1) {
3681 tableName = getDefine(tableName);
3682 if (ts.moveNext()) {
3684 if ((tmp.id() == PlsqlTokenId.DOT) && ts.moveNext()) {
3690 } else if (image.equalsIgnoreCase("COLUMN")) {
3692 moveNext = getNextNonWhitespace(ts, true);
3695 if (moveNext != false) {
3696 tableName = tmp.text().toString();
3697 if (tableName.indexOf('&') != -1) {
3701 tableName = getDefine(tableName);
3702 if (ts.moveNext()) {
3704 if ((tmp.id() == PlsqlTokenId.DOT) && ts.moveNext()) {
3710 ts.move(beginOffset);
3718 PlsqlTokenId tokenID = tmp.id();
3719 String image = tmp.text().toString();
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);
3729 //We have come to the end of the comment
3730 if ((tokenID == PlsqlTokenId.OPERATOR)
3731 && (image.equals(";") || (image.equals("/") && checkForOnlyChar(ts, ts.offset())))) {
3733 previousBlock = tmp;
3734 int offset = ts.offset();
3735 boolean mNext = getNextNonWhitespace(ts, false); //after ';' dont ignore comments
3737 Token<PlsqlTokenId> next = ts.token();
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
3745 block = new PlsqlBlock(commentBegin.offset(tokenHierarchy),
3746 previousBlock.offset(tokenHierarchy), tableName, alias, PlsqlBlockType.TABLE_COMMENT);
3748 block = new PlsqlBlock(commentBegin.offset(tokenHierarchy),
3749 previousBlock.offset(tokenHierarchy), tableName, alias, PlsqlBlockType.COLUMN_COMMENT);
3754 } else if ((tokenID == PlsqlTokenId.KEYWORD)) {
3755 if (image.equalsIgnoreCase("COLUMN")) {
3756 if (isTable == true) {
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));
3767 } else if (image.equalsIgnoreCase("TABLE")) {
3768 if (isTable == false) {
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));
3779 } else if (image.equalsIgnoreCase("COMMENT")) {
3781 moveNext = getNextNonWhitespace(ts, true);
3783 if (isEnd == false) {
3784 commentBegin = tmpPre;
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()) {
3798 PlsqlBlock custom = new PlsqlBlock(customStartToken.offset(tokenHierarchy),
3799 tmp.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
3800 customFoldBlocks.add(custom);
3803 customStartToken = null;
3806 PlsqlBlock child = checkComment(tmp, ts);
3807 if (child != null) {
3808 if (checkExisting(child, lstChild) == false) {
3809 lstChild.add(child);
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);
3824 moveNext = getNextNonWhitespace(ts, true);
3828 if (block != null) {
3830 addChildren(block, lstChild, parentBlocks);
3837 * Get Return next non whitespace token
3840 * @param ignoreComment: if true will ignore comments also
3843 private boolean getNextNonWhitespace(TokenSequence<PlsqlTokenId> ts, boolean ignoreComment) {
3844 boolean moveNext = ts.moveNext();
3845 Token<PlsqlTokenId> tmp = ts.token();
3849 if (tmp.id() == PlsqlTokenId.WHITESPACE) {
3850 moveNext = ts.moveNext();
3853 if ((ignoreComment == true) && (tmp.id() == PlsqlTokenId.LINE_COMMENT
3854 || tmp.id() == PlsqlTokenId.BLOCK_COMMENT)) {
3855 moveNext = ts.moveNext();
3867 * Get Return next non whitespace token
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()});
3877 if (tmp.id() == PlsqlTokenId.WHITESPACE && ("\n".equals(tmp.text()) || tmp.text().toString().contains("\n "))) {
3878 moveNext = ts.moveNext();
3888 * Return previous non whitespace token
3891 * @param ignoreComment
3894 private boolean getPreviousNonWhitespace(TokenSequence<PlsqlTokenId> ts, boolean ignoreComment) {
3895 boolean movePrevious = ts.movePrevious();
3896 Token<PlsqlTokenId> tmp = ts.token();
3898 while (movePrevious) {
3899 if (tmp.id() == PlsqlTokenId.WHITESPACE) {
3900 movePrevious = ts.movePrevious();
3903 if ((ignoreComment == true) && (tmp.id() == PlsqlTokenId.LINE_COMMENT
3904 || tmp.id() == PlsqlTokenId.BLOCK_COMMENT)) {
3905 movePrevious = ts.movePrevious();
3912 return movePrevious;
3916 * Check whether this is the start of a VIEW block
3920 * @param parentBlocks
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>();
3929 boolean moveNext = false;
3930 String viewName = "";
3931 int offset = ts.offset();
3933 //Check whether the beginning is in a SQL Plus command
3934 if (sqlPlusLine(ts)) {
3938 //second non whitespace character should be the name
3939 moveNext = getNextNonWhitespace(ts, true);
3940 if (moveNext == false) {
3948 //Second token is the view name
3949 viewName = tmp.text().toString().trim();
3950 viewName = checkForOtherSchema(ts, viewName);
3952 if (viewName.indexOf('&') != -1) {
3954 viewName = getDefine(viewName);
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();
3962 // //Move to next token
3963 // moveNext = getNextNonWhitespace(ts, true);
3964 // tmp = ts.token();
3968 Token<PlsqlTokenId> customStartToken = null;
3969 boolean isOk = false;
3972 String image = tmp.text().toString();
3973 PlsqlTokenId tokenID = tmp.id();
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(";"))) {
3980 if ((tokenID == PlsqlTokenId.KEYWORD) && (image.equalsIgnoreCase("AS"))) {
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())))) {
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);
3996 // alias = viewName;
3997 // viewName = getDefine(viewName);
4001 block = new PlsqlBlock(viewBegin.offset(tokenHierarchy),
4002 tmp.offset(tokenHierarchy), viewName, alias, PlsqlBlockType.VIEW);
4003 checkPrefix(viewBegin.offset(tokenHierarchy), ts, block);
4008 ts.moveNext(); // to avoid getting caught here again we have to move next
4011 } else if ((tokenID == PlsqlTokenId.KEYWORD)
4012 && ((image.equalsIgnoreCase("COMMENT"))
4013 || (image.equalsIgnoreCase("CREATE")))) { //Avoid catching ';' of other statements
4017 ts.moveNext(); // to avoid getting caught here again we have to move next
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()) {
4032 PlsqlBlock custom = new PlsqlBlock(customStartToken.offset(tokenHierarchy),
4033 tmp.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
4034 customFoldBlocks.add(custom);
4037 customStartToken = null;
4040 PlsqlBlock child = checkComment(tmp, ts);
4041 if (child != null) {
4042 if (checkExisting(child, lstChild) == false) {
4043 lstChild.add(child);
4047 moveNext = ts.moveNext();
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);
4058 moveNext = ts.moveNext();
4061 moveNext = ts.moveNext();
4066 if (block != null) {
4068 addChildren(block, lstChild, parentBlocks);
4075 * Get the name defined by &Name
4080 public String getDefine(
4082 String name = inputName;
4084 if (name.indexOf('&', 0) != -1) {
4085 String val = definesMap.get(name.substring(1).toUpperCase(Locale.ENGLISH));
4095 * Check &Name is in map as a key
4100 public boolean isDefine(String inputName) {
4101 String name = inputName;
4103 if (name.indexOf('&', 0) != -1) {
4104 return definesMap.containsKey(name.substring(1).toUpperCase(Locale.ENGLISH));
4111 * Check &Name is in map as a value
4116 public boolean hasDefineKey(String inputName) {
4117 String name = inputName;
4118 return definesMap.containsValue(name.toUpperCase(Locale.ENGLISH));
4122 * Get the key of &Name
4127 public String getDefineKey(String inputName) {
4128 for (Object o : definesMap.keySet()) {
4129 if (definesMap.get(o).equals(inputName)) {
4130 return o.toString();
4136 public Map<String, String> getDefines() {
4137 return Collections.unmodifiableMap(definesMap);
4141 * Method that will parse the document and initialize the aliases
4145 private void getAliases(Document doc) {
4146 TokenHierarchy tokenHier = TokenHierarchy.get(doc);
4147 @SuppressWarnings("unchecked")
4148 TokenSequence<PlsqlTokenId> ts = tokenHier.tokenSequence(PlsqlTokenId.language());
4153 //start to check aliases from the previous line end
4154 ts.move(startParse);
4155 Token<PlsqlTokenId> token = ts.token();
4157 //Get the difine by the name
4158 while (ts.moveNext() && ts.offset() <= endParse) {
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);
4171 StringTokenizer tokenizer = new StringTokenizer(tokenTxt);
4172 tokenizer.nextToken();
4175 boolean isNext = tokenizer.hasMoreTokens();
4179 alias = tokenizer.nextToken();
4184 isNext = tokenizer.hasMoreTokens();
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("/*")) {
4193 value = value + " " + temp;
4197 value = value.trim();
4199 if ((value.startsWith("\"") && value.endsWith("\""))
4200 || (value.startsWith("\'") && value.endsWith("\'"))) {
4201 value = value.substring(1, value.length() - 1);
4204 definesMap.put(alias.toUpperCase(Locale.ENGLISH), value);
4211 * Replace exStr in the given text with newStr
4213 * @param plsqlString
4218 public String replaceText(
4219 String plsqlString, String exStr, String newStr) {
4220 if (plsqlString.indexOf(exStr) >= 0) {
4221 plsqlString = plsqlString.replace(exStr, newStr);
4228 * Check whether the given offsets are in the same line
4235 private boolean checkSameLine(Document doc, int offset1, int offset2) {
4236 int startLine = offset2;
4237 int endLine = offset2;
4239 TokenHierarchy tokenHier = TokenHierarchy.get(doc);
4240 @SuppressWarnings("unchecked")
4241 TokenSequence<PlsqlTokenId> ts = tokenHier.tokenSequence(PlsqlTokenId.language());
4246 //go to the previous line break
4248 boolean movePrevious = ts.movePrevious();
4249 Token<PlsqlTokenId> tokenPre = ts.token();
4251 while (movePrevious) {
4252 if (tokenPre.text().toString().contains("\n")) {
4253 startLine = tokenPre.offset(tokenHier);
4257 movePrevious = ts.movePrevious();
4258 tokenPre = ts.token();
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();
4266 //go to the next line break
4268 boolean moveNext = ts.moveNext();
4269 Token<PlsqlTokenId> tokenNext = ts.token();
4272 if (tokenNext.text().toString().contains("\n")) {
4273 endLine = tokenNext.offset(tokenHier);
4277 moveNext = ts.moveNext();
4278 tokenNext = ts.token();
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();
4286 if ((offset1 >= startLine) && (offset1 <= endLine)) {
4287 if ((offset2 >= startLine) && (offset2 <= endLine)) {
4296 * Method that will check if blocks
4300 * @param parentBlocks
4303 private List<PlsqlBlock> checkIfBlock(Token<PlsqlTokenId> current, TokenSequence<PlsqlTokenId> ts, List<PlsqlBlock> parentBlocks) {
4304 Token<PlsqlTokenId> ifBegin = null;
4305 Token<PlsqlTokenId> token = null;
4307 List<PlsqlBlock> ifBlocks = new ArrayList<PlsqlBlock>();
4308 List<PlsqlBlock> lstChild = new ArrayList<PlsqlBlock>();
4309 boolean moveNext = false;
4311 //Check whether the beginning is in a SQL Plus command
4312 if (sqlPlusLine(ts)) {
4316 moveNext = ts.moveNext();
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")) {
4327 String image = token.text().toString();
4328 PlsqlTokenId tokenID = token.id();
4330 if ((token != null) && (!image.equals(";")) && (token.offset(tokenHierarchy) > endParse)) {
4334 if (image.equalsIgnoreCase("ELSE")) {
4336 PlsqlBlock block = new PlsqlBlock(ifBegin.offset(tokenHierarchy), preOffset, name.trim(), "", PlsqlBlockType.IF);
4337 if (block != null) {
4339 addChildren(block, lstChild, parentBlocks);
4340 ifBlocks.add(block);
4349 } else if (image.equalsIgnoreCase("ELSIF")) {
4351 PlsqlBlock block = new PlsqlBlock(ifBegin.offset(tokenHierarchy), preOffset, name.trim(), "", PlsqlBlockType.IF);
4352 if (block != null) {
4354 addChildren(block, lstChild, parentBlocks);
4355 ifBlocks.add(block);
4358 //reset everything for ELSE IF
4365 } else if (image.equalsIgnoreCase("END")) {
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(";")) {
4374 PlsqlBlock block = new PlsqlBlock(ifBegin.offset(tokenHierarchy), ts.offset(), name.trim(), "", PlsqlBlockType.IF);
4375 if (block != null) {
4377 addChildren(block, lstChild, parentBlocks);
4378 ifBlocks.add(block);
4389 } else if (image.equalsIgnoreCase("THEN")) {
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
4397 moveNext = ts.moveNext();
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);
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
4412 moveNext = ts.moveNext();
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);
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);
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
4434 moveNext = ts.moveNext();
4436 if (checkExisting(child, lstChild) == false) {
4437 lstChild.add(child);
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);
4449 moveNext = ts.moveNext();
4451 if (checkExisting(child, lstChild) == false) {
4452 lstChild.add(child);
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
4473 if (checkExisting(child, lstChild) == false) {
4474 lstChild.add(child);
4478 } else if (image.equalsIgnoreCase("PROCEDURE")
4479 || image.equalsIgnoreCase("FUNCTION")
4480 || image.equalsIgnoreCase("CREATE")) {
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()) {
4493 PlsqlBlock custom = new PlsqlBlock(customStartToken.offset(tokenHierarchy),
4494 token.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
4495 customFoldBlocks.add(custom);
4497 customStartToken = null;
4500 PlsqlBlock child = checkComment(token, ts);
4501 if (child != null) {
4502 if (checkExisting(child, lstChild) == false) {
4503 lstChild.add(child);
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);
4516 } else if (!isThen) {
4517 name = name + image;
4520 preOffset = ts.offset();
4521 moveNext = ts.moveNext();
4529 * Method that will check case blocks
4533 * @param parentBlocks
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;
4540 List<PlsqlBlock> caseBlocks = new ArrayList<PlsqlBlock>();
4541 List<PlsqlBlock> lstChild = new ArrayList<PlsqlBlock>();
4542 boolean moveNext = false;
4544 //Check whether the beginning is in a SQL Plus command
4545 if (!isStatement && sqlPlusLine(ts)) {
4549 moveNext = ts.moveNext();
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")) {
4561 String image = token.text().toString();
4562 PlsqlTokenId tokenID = token.id();
4564 if ((token != null) && (!image.equals(";")) && (token.offset(tokenHierarchy) > endParse)) {
4568 if (image.equalsIgnoreCase("ELSE")) {
4570 PlsqlBlock block = new PlsqlBlock(caseBegin.offset(tokenHierarchy), preOffset, name.trim(), "", PlsqlBlockType.CASE);
4571 if (block != null) {
4573 addChildren(block, lstChild, parentBlocks);
4574 caseBlocks.add(block);
4583 } else if (image.equalsIgnoreCase("WHEN")) {
4585 PlsqlBlock block = new PlsqlBlock(caseBegin.offset(tokenHierarchy), preOffset, name.trim(), "", PlsqlBlockType.CASE);
4586 if (block != null) {
4588 addChildren(block, lstChild, parentBlocks);
4589 caseBlocks.add(block);
4592 //reset everything for ELSE IF
4596 } else if (name.trim().startsWith("CASE")) { //first WHEN
4597 name = name + image;
4601 } else if (image.equalsIgnoreCase("END")) {
4603 boolean next = false;
4604 Token<PlsqlTokenId> nextTok = token;
4605 int offset = ts.offset();
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();
4613 if (!(!isStatement && next && nextTok.text().toString().equalsIgnoreCase(";"))) {
4619 PlsqlBlock block = new PlsqlBlock(caseBegin.offset(tokenHierarchy), ts.offset(), name.trim(), "", PlsqlBlockType.CASE);
4620 if (block != null) {
4622 addChildren(block, lstChild, parentBlocks);
4623 caseBlocks.add(block);
4625 if (isStatement && ts.token().toString().equals(";")) {
4633 } else if (image.equalsIgnoreCase("THEN")) {
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
4641 moveNext = ts.moveNext();
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);
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
4656 moveNext = ts.moveNext();
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);
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);
4674 moveNext = ts.moveNext();
4676 if (checkExisting(child, lstChild) == false) {
4677 lstChild.add(child);
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
4687 moveNext = ts.moveNext();
4689 if (checkExisting(child, lstChild) == false) {
4690 lstChild.add(child);
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
4710 if (checkExisting(child, lstChild) == false) {
4711 lstChild.add(child);
4715 } else if (image.equalsIgnoreCase("PROCEDURE")
4716 || image.equalsIgnoreCase("FUNCTION")
4717 || image.equalsIgnoreCase("CREATE")) {
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()) {
4727 PlsqlBlock custom = new PlsqlBlock(customStartToken.offset(tokenHierarchy),
4728 token.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
4729 customFoldBlocks.add(custom);
4731 customStartToken = null;
4734 PlsqlBlock child = checkComment(token, ts);
4735 if (child != null) {
4736 if (checkExisting(child, lstChild) == false) {
4737 lstChild.add(child);
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);
4748 } else if (!isThen) {
4749 name = name + image;
4752 preOffset = ts.offset();
4753 moveNext = ts.moveNext();
4761 * Method that will return the prefix of the given block
4763 * @param startOffset
4767 private String getPreceedingText(int startOffset, TokenSequence<PlsqlTokenId> ts) {
4769 int offset = ts.offset();
4770 ts.move(startOffset);
4772 Token<PlsqlTokenId> token = ts.token();
4774 while (ts.movePrevious()) {
4776 String image = token.text().toString();
4778 if (image.contains("\n")) {
4782 prefix = token.text().toString() + prefix;
4791 * Method that will check loop blocks
4795 * @param parentBlocks
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;
4805 //Check whether the beginning is in a SQL Plus command
4806 if (sqlPlusLine(ts)) {
4810 moveNext = ts.moveNext();
4812 loopBegin = current;
4813 boolean isLoop = false;
4814 Token<PlsqlTokenId> customStartToken = null;
4815 PlsqlBlockType type = PlsqlBlockType.LOOP;
4817 if (loopBegin.text().toString().equalsIgnoreCase("LOOP")) {
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;
4826 String image = token.text().toString();
4827 PlsqlTokenId tokenID = token.id();
4829 if ((token != null) && (!image.equals(";")) && (token.offset(tokenHierarchy) > endParse)) {
4833 if (!isLoop && image.equalsIgnoreCase("LOOP")) {
4835 } else if (image.equalsIgnoreCase("END")) {
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(";")) {
4844 block = new PlsqlBlock(loopBegin.offset(tokenHierarchy), ts.offset(), name.trim(), "", type);
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
4859 moveNext = ts.moveNext();
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);
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
4874 moveNext = ts.moveNext();
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);
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);
4892 moveNext = ts.moveNext();
4894 if (checkExisting(child, lstChild) == false) {
4895 lstChild.add(child);
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
4916 if (checkExisting(child, lstChild) == false) {
4917 lstChild.add(child);
4921 } else if (image.equalsIgnoreCase("PROCEDURE")
4922 || image.equalsIgnoreCase("FUNCTION")
4923 || image.equalsIgnoreCase("CREATE")) {
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()) {
4933 PlsqlBlock custom = new PlsqlBlock(customStartToken.offset(tokenHierarchy),
4934 token.offset(tokenHierarchy), name, "", PlsqlBlockType.CUSTOM_FOLD);
4935 customFoldBlocks.add(custom);
4938 customStartToken = null;
4941 PlsqlBlock child = checkComment(token, ts);
4942 if (child != null) {
4943 if (checkExisting(child, lstChild) == false) {
4944 lstChild.add(child);
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);
4957 } else if (!isLoop) {
4958 name = name + image;
4961 moveNext = ts.moveNext();
4965 if (block != null) {
4967 addChildren(block, lstChild, parentBlocks);
4974 * Method that will add the given child blocks to the block and remove from parent blocks if existing there
4978 * @param parentBlocks
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);
4989 public int getStartParse() {
4993 public int getEndParse() {
4997 public int getChangedLength() {
4998 return changedLength;