EADS-3506 java.lang.IllegalStateException The manager has been already released SEVERE
authorchrislovsund@netbeans.org
Mon, 11 Mar 2013 15:26:32 +0100
changeset 355a540b65e3395
parent 354 21c221d471ec
child 356 e96211b127ae
EADS-3506 java.lang.IllegalStateException The manager has been already released SEVERE
Looking at org.netbeans.modules.editor.lib.CustomFoldManager to stop all kinds of exceptions from being thrown.
Most thing seems to work as before, minus all the thrown exceptions.
Still getting: "Adding a fold that is identical with another previously added fold from the same FoldManager is not allowed." when adding all PlsqlBlocks every time
PLSQL/Folding/nbproject/project.xml
PLSQL/Folding/src/org/netbeans/modules/plsql/fold/CustomFoldManager.java
PLSQL/Folding/src/org/netbeans/modules/plsql/fold/NewPlsqlFoldManager.java
PLSQL/Folding/src/org/netbeans/modules/plsql/fold/PlsqlFoldManager.java
PLSQL/Folding/src/org/netbeans/modules/plsql/fold/PlsqlFoldManagerFactory.java
PLSQL/Lexer/src/org/netbeans/modules/plsql/lexer/PlsqlBlockFactory.java
     1.1 --- a/PLSQL/Folding/nbproject/project.xml	Wed Mar 06 17:14:40 2013 +0100
     1.2 +++ b/PLSQL/Folding/nbproject/project.xml	Mon Mar 11 15:26:32 2013 +0100
     1.3 @@ -7,22 +7,6 @@
     1.4              <suite-component/>
     1.5              <module-dependencies>
     1.6                  <dependency>
     1.7 -                    <code-name-base>org.netbeans.modules.plsqlsupport.options</code-name-base>
     1.8 -                    <build-prerequisite/>
     1.9 -                    <compile-dependency/>
    1.10 -                    <run-dependency>
    1.11 -                        <specification-version>1.0.1</specification-version>
    1.12 -                    </run-dependency>
    1.13 -                </dependency>
    1.14 -                <dependency>
    1.15 -                    <code-name-base>org.netbeans.modules.plsql.lexer</code-name-base>
    1.16 -                    <build-prerequisite/>
    1.17 -                    <compile-dependency/>
    1.18 -                    <run-dependency>
    1.19 -                        <specification-version>1.0</specification-version>
    1.20 -                    </run-dependency>
    1.21 -                </dependency>
    1.22 -                <dependency>
    1.23                      <code-name-base>org.netbeans.modules.editor</code-name-base>
    1.24                      <build-prerequisite/>
    1.25                      <compile-dependency/>
    1.26 @@ -50,6 +34,31 @@
    1.27                      </run-dependency>
    1.28                  </dependency>
    1.29                  <dependency>
    1.30 +                    <code-name-base>org.netbeans.modules.lexer</code-name-base>
    1.31 +                    <build-prerequisite/>
    1.32 +                    <compile-dependency/>
    1.33 +                    <run-dependency>
    1.34 +                        <release-version>2</release-version>
    1.35 +                        <specification-version>1.46.1.1</specification-version>
    1.36 +                    </run-dependency>
    1.37 +                </dependency>
    1.38 +                <dependency>
    1.39 +                    <code-name-base>org.netbeans.modules.plsql.lexer</code-name-base>
    1.40 +                    <build-prerequisite/>
    1.41 +                    <compile-dependency/>
    1.42 +                    <run-dependency>
    1.43 +                        <specification-version>1.0</specification-version>
    1.44 +                    </run-dependency>
    1.45 +                </dependency>
    1.46 +                <dependency>
    1.47 +                    <code-name-base>org.netbeans.modules.plsqlsupport.options</code-name-base>
    1.48 +                    <build-prerequisite/>
    1.49 +                    <compile-dependency/>
    1.50 +                    <run-dependency>
    1.51 +                        <specification-version>1.0.1</specification-version>
    1.52 +                    </run-dependency>
    1.53 +                </dependency>
    1.54 +                <dependency>
    1.55                      <code-name-base>org.openide.loaders</code-name-base>
    1.56                      <build-prerequisite/>
    1.57                      <compile-dependency/>
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/PLSQL/Folding/src/org/netbeans/modules/plsql/fold/CustomFoldManager.java	Mon Mar 11 15:26:32 2013 +0100
     2.3 @@ -0,0 +1,759 @@
     2.4 +/*
     2.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     2.6 + *
     2.7 + * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
     2.8 + *
     2.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    2.10 + * Other names may be trademarks of their respective owners.
    2.11 + *
    2.12 + * The contents of this file are subject to the terms of either the GNU
    2.13 + * General Public License Version 2 only ("GPL") or the Common
    2.14 + * Development and Distribution License("CDDL") (collectively, the
    2.15 + * "License"). You may not use this file except in compliance with the
    2.16 + * License. You can obtain a copy of the License at
    2.17 + * http://www.netbeans.org/cddl-gplv2.html
    2.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    2.19 + * specific language governing permissions and limitations under the
    2.20 + * License.  When distributing the software, include this License Header
    2.21 + * Notice in each file and include the License file at
    2.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    2.23 + * particular file as subject to the "Classpath" exception as provided
    2.24 + * by Oracle in the GPL Version 2 section of the License file that
    2.25 + * accompanied this code. If applicable, add the following below the
    2.26 + * License Header, with the fields enclosed by brackets [] replaced by
    2.27 + * your own identifying information:
    2.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    2.29 + *
    2.30 + * Contributor(s):
    2.31 + *
    2.32 + * The Original Software is NetBeans. The Initial Developer of the Original
    2.33 + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
    2.34 + * Microsystems, Inc. All Rights Reserved.
    2.35 + *
    2.36 + * If you wish your version of this file to be governed by only the CDDL
    2.37 + * or only the GPL Version 2, indicate your decision by adding
    2.38 + * "[Contributor] elects to include this software in this distribution
    2.39 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    2.40 + * single choice of license, a recipient has the option to distribute
    2.41 + * your version of this file under either the CDDL, the GPL Version 2 or
    2.42 + * to extend the choice of license to its licensees as provided above.
    2.43 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    2.44 + * Version 2 license, then the option applies only if the new code is
    2.45 + * made subject to such option by the copyright holder.
    2.46 + */
    2.47 +
    2.48 +package org.netbeans.modules.plsql.fold;
    2.49 +
    2.50 +import javax.swing.text.Document;
    2.51 +import javax.swing.text.BadLocationException;
    2.52 +import javax.swing.text.Position;
    2.53 +import javax.swing.event.DocumentEvent;
    2.54 +import java.util.*;
    2.55 +import java.util.logging.Level;
    2.56 +import java.util.logging.Logger;
    2.57 +import java.util.regex.Pattern;
    2.58 +import java.util.regex.Matcher;
    2.59 +import org.netbeans.api.editor.fold.Fold;
    2.60 +import org.netbeans.api.editor.fold.FoldHierarchy;
    2.61 +import org.netbeans.api.editor.fold.FoldType;
    2.62 +import org.netbeans.api.lexer.Token;
    2.63 +import org.netbeans.api.lexer.TokenHierarchy;
    2.64 +import org.netbeans.api.lexer.TokenSequence;
    2.65 +import org.netbeans.editor.BaseDocument;
    2.66 +import org.netbeans.spi.editor.fold.FoldHierarchyTransaction;
    2.67 +import org.netbeans.spi.editor.fold.FoldManager;
    2.68 +import org.netbeans.spi.editor.fold.FoldManagerFactory;
    2.69 +import org.netbeans.spi.editor.fold.FoldOperation;
    2.70 +import org.openide.util.RequestProcessor;
    2.71 +
    2.72 +/**
    2.73 + * Fold maintainer that creates and updates custom folds.
    2.74 + *
    2.75 + * @author Dusan Balek, Miloslav Metelka
    2.76 + * @version 1.00
    2.77 + */
    2.78 +
    2.79 +final class CustomFoldManager implements FoldManager, Runnable {
    2.80 +    
    2.81 +    private static final Logger LOG = Logger.getLogger(CustomFoldManager.class.getName());
    2.82 +    
    2.83 +    public static final FoldType CUSTOM_FOLD_TYPE = new FoldType("custom-fold"); // NOI18N
    2.84 +
    2.85 +    private FoldOperation operation;
    2.86 +    private Document doc;
    2.87 +    private org.netbeans.editor.GapObjectArray markArray = new org.netbeans.editor.GapObjectArray();
    2.88 +    private int minUpdateMarkOffset = Integer.MAX_VALUE;
    2.89 +    private int maxUpdateMarkOffset = -1;
    2.90 +    private List removedFoldList;
    2.91 +    private HashMap customFoldId = new HashMap();
    2.92 +
    2.93 +    private static final RequestProcessor RP = new RequestProcessor(CustomFoldManager.class.getName(),
    2.94 +            1, false, false);
    2.95 +    private final RequestProcessor.Task task = RP.create(this);
    2.96 +
    2.97 +    public void init(FoldOperation operation) {
    2.98 +        this.operation = operation;
    2.99 +        if (LOG.isLoggable(Level.FINE)) {
   2.100 +            LOG.log(Level.FINE, "Initialized: {0}", System.identityHashCode(this));
   2.101 +        }
   2.102 +    }
   2.103 +    
   2.104 +    private FoldOperation getOperation() {
   2.105 +        return operation;
   2.106 +    }
   2.107 +
   2.108 +    public void initFolds(FoldHierarchyTransaction transaction) {
   2.109 +        doc = getOperation().getHierarchy().getComponent().getDocument();
   2.110 +        task.schedule(300);
   2.111 +    }
   2.112 +
   2.113 +    public void insertUpdate(DocumentEvent evt, FoldHierarchyTransaction transaction) {
   2.114 +        processRemovedFolds(transaction);
   2.115 +        task.schedule(300);
   2.116 +    }
   2.117 +
   2.118 +    public void removeUpdate(DocumentEvent evt, FoldHierarchyTransaction transaction) {
   2.119 +        processRemovedFolds(transaction);
   2.120 +        removeAffectedMarks(evt, transaction);
   2.121 +        task.schedule(300);
   2.122 +    }
   2.123 +    
   2.124 +    public void changedUpdate(DocumentEvent evt, FoldHierarchyTransaction transaction) {
   2.125 +    }
   2.126 +    
   2.127 +    public void removeEmptyNotify(Fold emptyFold) {
   2.128 +        removeFoldNotify(emptyFold);
   2.129 +    }
   2.130 +    
   2.131 +    public void removeDamagedNotify(Fold damagedFold) {
   2.132 +        removeFoldNotify(damagedFold);
   2.133 +    }
   2.134 +    
   2.135 +    public void expandNotify(Fold expandedFold) {
   2.136 +        
   2.137 +    }
   2.138 +
   2.139 +    public void release() {
   2.140 +        if (LOG.isLoggable(Level.FINE)) {
   2.141 +            LOG.log(Level.FINE, "Released: {0}", System.identityHashCode(this));
   2.142 +        }
   2.143 +    }
   2.144 +
   2.145 +    public void run() {
   2.146 +        if (operation.isReleased()) {
   2.147 +            if (LOG.isLoggable(Level.FINE)) {
   2.148 +                LOG.log(Level.FINE, "Update skipped, already relaesed: {0}", System.identityHashCode(this));
   2.149 +            }
   2.150 +            return;
   2.151 +        }
   2.152 +        ((BaseDocument) doc).readLock();
   2.153 +        try {
   2.154 +            TokenHierarchy th = TokenHierarchy.get(doc);
   2.155 +            if (th != null && th.isActive()) {
   2.156 +                FoldHierarchy hierarchy = getOperation().getHierarchy();
   2.157 +                hierarchy.lock();
   2.158 +                try {
   2.159 +                    if (operation.isReleased()) {
   2.160 +                        if (LOG.isLoggable(Level.FINE)) {
   2.161 +                            LOG.log(Level.FINE, "Update skipped, already relaesed: {0}", System.identityHashCode(this));
   2.162 +                        }
   2.163 +                        return;
   2.164 +                    }
   2.165 +                    if (LOG.isLoggable(Level.FINE)) {
   2.166 +                        LOG.log(Level.FINE, "Updating: {0}", System.identityHashCode(this));
   2.167 +                    }
   2.168 +                    FoldHierarchyTransaction transaction = getOperation().openTransaction();
   2.169 +                    try {
   2.170 +                        updateFolds(th.tokenSequence(), transaction);
   2.171 +                    } finally {
   2.172 +                        transaction.commit();
   2.173 +                    }
   2.174 +                } finally {
   2.175 +                    hierarchy.unlock();
   2.176 +                }
   2.177 +            }
   2.178 +        } finally {
   2.179 +            ((BaseDocument) doc).readUnlock();
   2.180 +        }
   2.181 +    }
   2.182 +    
   2.183 +    private void removeFoldNotify(Fold removedFold) {
   2.184 +        if (removedFoldList == null) {
   2.185 +            removedFoldList = new ArrayList(3);
   2.186 +        }
   2.187 +        removedFoldList.add(removedFold);
   2.188 +    }
   2.189 +    
   2.190 +    private void removeAffectedMarks(DocumentEvent evt, FoldHierarchyTransaction transaction) {
   2.191 +        int removeOffset = evt.getOffset();
   2.192 +        int markIndex = findMarkIndex(removeOffset);
   2.193 +        if (markIndex < getMarkCount()) {
   2.194 +            FoldMarkInfo mark;
   2.195 +            while (markIndex >= 0 && (mark = getMark(markIndex)).getOffset() == removeOffset) {
   2.196 +                mark.release(false, transaction);
   2.197 +                removeMark(markIndex);
   2.198 +                markIndex--;
   2.199 +            }
   2.200 +        }
   2.201 +    }
   2.202 +    
   2.203 +    private void processRemovedFolds(FoldHierarchyTransaction transaction) {
   2.204 +        if (removedFoldList != null) {
   2.205 +            for (int i = removedFoldList.size() - 1; i >= 0; i--) {
   2.206 +                Fold removedFold = (Fold)removedFoldList.get(i);
   2.207 +                FoldMarkInfo startMark = (FoldMarkInfo)getOperation().getExtraInfo(removedFold);
   2.208 +                if (startMark.getId() != null)
   2.209 +                    customFoldId.put(startMark.getId(), Boolean.valueOf(removedFold.isCollapsed())); // remember the last fold's state before remove
   2.210 +                FoldMarkInfo endMark = startMark.getPairMark(); // get prior releasing
   2.211 +                if (getOperation().isStartDamaged(removedFold)) { // start mark area was damaged
   2.212 +                    startMark.release(true, transaction); // forced remove
   2.213 +                }
   2.214 +                if (getOperation().isEndDamaged(removedFold)) {
   2.215 +                    endMark.release(true, transaction);
   2.216 +                }
   2.217 +            }
   2.218 +        }
   2.219 +        removedFoldList = null;
   2.220 +    }
   2.221 +
   2.222 +    private void markUpdate(FoldMarkInfo mark) {
   2.223 +        markUpdate(mark.getOffset());
   2.224 +    }
   2.225 +    
   2.226 +    private void markUpdate(int offset) {
   2.227 +        if (offset < minUpdateMarkOffset) {
   2.228 +            minUpdateMarkOffset = offset;
   2.229 +        }
   2.230 +        if (offset > maxUpdateMarkOffset) {
   2.231 +            maxUpdateMarkOffset = offset;
   2.232 +        }
   2.233 +    }
   2.234 +    
   2.235 +    private FoldMarkInfo getMark(int index) {
   2.236 +        return (FoldMarkInfo)markArray.getItem(index);
   2.237 +    }
   2.238 +    
   2.239 +    private int getMarkCount() {
   2.240 +        return markArray.getItemCount();
   2.241 +    }
   2.242 +    
   2.243 +    private void removeMark(int index) {
   2.244 +        if (LOG.isLoggable(Level.FINE)) {
   2.245 +            LOG.fine("Removing mark from ind=" + index + ": " + getMark(index)); // NOI18N
   2.246 +        }
   2.247 +        markArray.remove(index, 1);
   2.248 +    }
   2.249 +    
   2.250 +    private void insertMark(int index, FoldMarkInfo mark) {
   2.251 +        markArray.insertItem(index, mark);
   2.252 +        if (LOG.isLoggable(Level.FINE)) {
   2.253 +            LOG.fine("Inserted mark at ind=" + index + ": " + mark); // NOI18N
   2.254 +        }
   2.255 +    }
   2.256 +
   2.257 +    private int findMarkIndex(int offset) {
   2.258 +        int markCount = getMarkCount();
   2.259 +        int low = 0;
   2.260 +        int high = markCount - 1;
   2.261 +        
   2.262 +        while (low <= high) {
   2.263 +            int mid = (low + high) / 2;
   2.264 +            int midMarkOffset = getMark(mid).getOffset();
   2.265 +            
   2.266 +            if (midMarkOffset < offset) {
   2.267 +                low = mid + 1;
   2.268 +            } else if (midMarkOffset > offset) {
   2.269 +                high = mid - 1;
   2.270 +            } else {
   2.271 +                // mark starting exactly at the given offset found
   2.272 +                // If multiple -> find the one with highest index
   2.273 +                mid++;
   2.274 +                while (mid < markCount && getMark(mid).getOffset() == offset) {
   2.275 +                    mid++;
   2.276 +                }
   2.277 +                mid--;
   2.278 +                return mid;
   2.279 +            }
   2.280 +        }
   2.281 +        return low; // return higher index (e.g. for insert)
   2.282 +    }
   2.283 +    
   2.284 +    private List<FoldMarkInfo> getMarkList(TokenSequence seq) {
   2.285 +        List<FoldMarkInfo> markList = null;
   2.286 +        
   2.287 +        for(seq.moveStart(); seq.moveNext(); ) {
   2.288 +            Token token = seq.token();
   2.289 +            FoldMarkInfo info;
   2.290 +            try {
   2.291 +                info = scanToken(token);
   2.292 +            } catch (BadLocationException e) {
   2.293 +                LOG.log(Level.WARNING, null, e);
   2.294 +                info = null;
   2.295 +            }
   2.296 +
   2.297 +            if (info != null) {
   2.298 +                if (markList == null) {
   2.299 +                    markList = new ArrayList<FoldMarkInfo>();
   2.300 +                }
   2.301 +                markList.add(info);
   2.302 +            }
   2.303 +        }
   2.304 +
   2.305 +        return markList;
   2.306 +    }
   2.307 +    
   2.308 +    private void processTokenList(TokenSequence seq, FoldHierarchyTransaction transaction) {
   2.309 +        List<FoldMarkInfo> markList = getMarkList(seq);
   2.310 +        int markListSize;
   2.311 +        if (markList != null && ((markListSize = markList.size()) > 0)) {
   2.312 +            // Find the index for insertion
   2.313 +            int offset = ((FoldMarkInfo)markList.get(0)).getOffset();
   2.314 +            int arrayMarkIndex = findMarkIndex(offset);
   2.315 +            // Remember the corresponding mark in the array as well
   2.316 +            FoldMarkInfo arrayMark;
   2.317 +            int arrayMarkOffset;
   2.318 +            if (arrayMarkIndex < getMarkCount()) {
   2.319 +                arrayMark = getMark(arrayMarkIndex);
   2.320 +                arrayMarkOffset = arrayMark.getOffset();
   2.321 +            } else { // at last mark
   2.322 +                arrayMark = null;
   2.323 +                arrayMarkOffset = Integer.MAX_VALUE;
   2.324 +            }
   2.325 +
   2.326 +            for (int i = 0; i < markListSize; i++) {
   2.327 +                FoldMarkInfo listMark = (FoldMarkInfo)markList.get(i);
   2.328 +                int listMarkOffset = listMark.getOffset();
   2.329 +                if (i == 0 || i == markListSize - 1) {
   2.330 +                    // Update the update-offsets by the first and last marks in the list
   2.331 +                    markUpdate(listMarkOffset);
   2.332 +                }
   2.333 +                while (listMarkOffset >= arrayMarkOffset) {
   2.334 +                    if (listMarkOffset == arrayMarkOffset) {
   2.335 +                        // At the same offset - likely the same mark
   2.336 +                        //   -> retain the collapsed state
   2.337 +                        listMark.setCollapsed(arrayMark.isCollapsed());
   2.338 +                    }
   2.339 +                    if (!arrayMark.isReleased()) { // make sure that the mark is released
   2.340 +                        arrayMark.release(false, transaction); 
   2.341 +                    }
   2.342 +                    removeMark(arrayMarkIndex);
   2.343 +                    if (LOG.isLoggable(Level.FINE)) {
   2.344 +                        LOG.fine("Removed dup mark from ind=" + arrayMarkIndex + ": " + arrayMark); // NOI18N
   2.345 +                    }
   2.346 +                    if (arrayMarkIndex < getMarkCount()) {
   2.347 +                        arrayMark = getMark(arrayMarkIndex);
   2.348 +                        arrayMarkOffset = arrayMark.getOffset();
   2.349 +                    } else { // no more marks
   2.350 +                        arrayMark = null;
   2.351 +                        arrayMarkOffset = Integer.MAX_VALUE;
   2.352 +                    }
   2.353 +                }
   2.354 +                // Insert the listmark
   2.355 +                insertMark(arrayMarkIndex, listMark);
   2.356 +                if (LOG.isLoggable(Level.FINE)) {
   2.357 +                    LOG.fine("Inserted mark at ind=" + arrayMarkIndex + ": " + listMark); // NOI18N
   2.358 +                }
   2.359 +                arrayMarkIndex++;
   2.360 +            }
   2.361 +        }
   2.362 +    }
   2.363 +
   2.364 +    private void updateFolds(TokenSequence seq, FoldHierarchyTransaction transaction) {
   2.365 +
   2.366 +        if (seq != null && !seq.isEmpty()) {
   2.367 +            processTokenList(seq, transaction);
   2.368 +        }
   2.369 +
   2.370 +        if (maxUpdateMarkOffset == -1) { // no updates
   2.371 +            return;
   2.372 +        }
   2.373 +        
   2.374 +        // Find the first mark to update and init the prevMark and parentMark prior the loop
   2.375 +        int index = findMarkIndex(minUpdateMarkOffset);
   2.376 +        FoldMarkInfo prevMark;
   2.377 +        FoldMarkInfo parentMark;
   2.378 +        if (index == 0) { // start from begining
   2.379 +            prevMark = null;
   2.380 +            parentMark = null;
   2.381 +        } else {
   2.382 +            prevMark = getMark(index - 1);
   2.383 +            parentMark = prevMark.getParentMark();
   2.384 +        }
   2.385 +        
   2.386 +        // Iterate through the changed marks in the mark array 
   2.387 +        int markCount = getMarkCount();
   2.388 +        while (index < markCount) { // process the marks
   2.389 +            FoldMarkInfo mark = getMark(index);
   2.390 +
   2.391 +            // If the mark was released then it must be removed
   2.392 +            if (mark.isReleased()) {
   2.393 +                if (LOG.isLoggable(Level.FINE)) {
   2.394 +                    LOG.fine("Removing released mark at ind=" + index + ": " + mark); // NOI18N
   2.395 +                }
   2.396 +                removeMark(index);
   2.397 +                markCount--;
   2.398 +                continue;
   2.399 +            }
   2.400 +
   2.401 +            // Update mark's status (folds, parentMark etc.)
   2.402 +            if (mark.isStartMark()) { // starting a new fold
   2.403 +                if (prevMark == null || prevMark.isStartMark()) { // new level
   2.404 +                    mark.setParentMark(prevMark); // prevMark == null means root level
   2.405 +                    parentMark = prevMark;
   2.406 +
   2.407 +                } // same level => parent to the parent of the prevMark
   2.408 +
   2.409 +            } else { // end mark
   2.410 +                if (prevMark != null) {
   2.411 +                    if (prevMark.isStartMark()) { // closing nearest fold
   2.412 +                        prevMark.setEndMark(mark, false, transaction);
   2.413 +
   2.414 +                    } else { // prevMark is end mark - closing its parent fold
   2.415 +                        if (parentMark != null) {
   2.416 +                            // mark's parent gets set as well
   2.417 +                            parentMark.setEndMark(mark, false, transaction);
   2.418 +                            parentMark = parentMark.getParentMark();
   2.419 +
   2.420 +                        } else { // prevMark's parentMark is null (top level)
   2.421 +                            mark.makeSolitaire(false, transaction);
   2.422 +                        }
   2.423 +                    }
   2.424 +                    
   2.425 +                } else { // prevMark is null
   2.426 +                    mark.makeSolitaire(false, transaction);
   2.427 +                }
   2.428 +            }
   2.429 +
   2.430 +            // Set parent mark of the mark
   2.431 +            mark.setParentMark(parentMark);
   2.432 +
   2.433 +            
   2.434 +            prevMark = mark;
   2.435 +            index++;
   2.436 +        }
   2.437 +
   2.438 +        minUpdateMarkOffset = Integer.MAX_VALUE;
   2.439 +        maxUpdateMarkOffset = -1;
   2.440 +        
   2.441 +        if (LOG.isLoggable(Level.FINE)) {
   2.442 +            LOG.fine("MARKS DUMP:\n" + this); //NOI18N
   2.443 +        }
   2.444 +    }
   2.445 +    
   2.446 +    public @Override String toString() {
   2.447 +        StringBuffer sb = new StringBuffer();
   2.448 +        int markCount = getMarkCount();
   2.449 +        int markCountDigitCount = Integer.toString(markCount).length();
   2.450 +        for (int i = 0; i < markCount; i++) {
   2.451 +            sb.append("["); // NOI18N
   2.452 +            String iStr = Integer.toString(i);
   2.453 +            appendSpaces(sb, markCountDigitCount - iStr.length());
   2.454 +            sb.append(iStr);
   2.455 +            sb.append("]:"); // NOI18N
   2.456 +            FoldMarkInfo mark = getMark(i);
   2.457 +            
   2.458 +            // Add extra indent regarding the depth in hierarchy
   2.459 +            int indent = 0;
   2.460 +            FoldMarkInfo parentMark = mark.getParentMark();
   2.461 +            while (parentMark != null) {
   2.462 +                indent += 4;
   2.463 +                parentMark = parentMark.getParentMark();
   2.464 +            }
   2.465 +            appendSpaces(sb, indent);
   2.466 +
   2.467 +            sb.append(mark);
   2.468 +            sb.append('\n');
   2.469 +        }
   2.470 +        return sb.toString();
   2.471 +    }
   2.472 +    
   2.473 +    private static void appendSpaces(StringBuffer sb, int spaces) {
   2.474 +        while (--spaces >= 0) {
   2.475 +            sb.append(' ');
   2.476 +        }
   2.477 +    }
   2.478 +
   2.479 +    private static Pattern pattern = Pattern.compile(
   2.480 +            "(<\\s*editor-fold" +
   2.481 +            // id="x"[opt] defaultstate="y"[opt] desc="z"[opt] defaultstate="a"[opt]
   2.482 +            // id must be first, the rest of attributes in random order
   2.483 +            "(?:(?:\\s+id=\"(\\S*)\")?(?:\\s+defaultstate=\"(\\S*?)\")?(?:\\s+desc=\"([\\S \\t]*?)\")?(?:\\s+defaultstate=\"(\\S*?)\")?)" +
   2.484 +            "\\s*>)|(?:</\\s*editor-fold\\s*>)"); // NOI18N
   2.485 +
   2.486 +    private FoldMarkInfo scanToken(Token token) throws BadLocationException {
   2.487 +        // ignore any token that is not comment
   2.488 +        if (token.id().primaryCategory() != null && "comment".equals(token.id().primaryCategory())) { //NOI18N
   2.489 +            Matcher matcher = pattern.matcher(token.text());
   2.490 +            if (matcher.find()) {
   2.491 +                if (matcher.group(1) != null) { // fold's start mark found
   2.492 +                    boolean state;
   2.493 +                    if (matcher.group(3) != null) {
   2.494 +                        state = "collapsed".equals(matcher.group(3)); // remember the defaultstate // NOI18N
   2.495 +                    } else {
   2.496 +                        state = "collapsed".equals(matcher.group(5));
   2.497 +                    }
   2.498 +                    
   2.499 +                    if (matcher.group(2) != null) { // fold's id exists
   2.500 +                        Boolean collapsed = (Boolean)customFoldId.get(matcher.group(2));
   2.501 +                        if (collapsed != null)
   2.502 +                            state = collapsed.booleanValue(); // fold's state is already known from the past
   2.503 +                        else
   2.504 +                            customFoldId.put(matcher.group(2), Boolean.valueOf(state));
   2.505 +                    }
   2.506 +                    return new FoldMarkInfo(true, token.offset(null), matcher.end(0), matcher.group(2), state, matcher.group(4)); // NOI18N
   2.507 +                } else { // fold's end mark found
   2.508 +                    return new FoldMarkInfo(false, token.offset(null), matcher.end(0), null, false, null);
   2.509 +                }
   2.510 +            }
   2.511 +        }
   2.512 +        return null;
   2.513 +    }
   2.514 +
   2.515 +    private final class FoldMarkInfo {
   2.516 +
   2.517 +        private boolean startMark;
   2.518 +        private Position pos;
   2.519 +        private int length;
   2.520 +        private String id;
   2.521 +        private boolean collapsed;
   2.522 +        private String description;
   2.523 +
   2.524 +        /** Matching pair mark used for fold construction */
   2.525 +        private FoldMarkInfo pairMark;
   2.526 +        
   2.527 +        /** Parent mark defining nesting in the mark hierarchy. */
   2.528 +        private FoldMarkInfo parentMark;
   2.529 +        
   2.530 +        /**
   2.531 +         * Fold that corresponds to this mark (if it's start mark).
   2.532 +         * It can be null if this mark is end mark or if it currently
   2.533 +         * does not have the fold assigned.
   2.534 +         */
   2.535 +        private Fold fold;
   2.536 +        
   2.537 +        private boolean released;
   2.538 +        
   2.539 +        private FoldMarkInfo(boolean startMark, int offset,
   2.540 +                             int length, String id, boolean collapsed, String description)
   2.541 +        throws BadLocationException {
   2.542 +
   2.543 +            this.startMark = startMark;
   2.544 +            this.pos = doc.createPosition(offset);
   2.545 +            this.length = length;
   2.546 +            this.id = id;
   2.547 +            this.collapsed = collapsed;
   2.548 +            this.description = description;
   2.549 +        }
   2.550 +
   2.551 +        public String getId() {
   2.552 +            return id;
   2.553 +        }
   2.554 +
   2.555 +        public String getDescription() {
   2.556 +            return description;
   2.557 +        }
   2.558 +
   2.559 +        public boolean isStartMark() {
   2.560 +            return startMark;
   2.561 +        }
   2.562 +
   2.563 +        public int getLength() {
   2.564 +            return length;
   2.565 +        }
   2.566 +
   2.567 +        public int getOffset() {
   2.568 +            return pos.getOffset();
   2.569 +        }
   2.570 +        
   2.571 +        public int getEndOffset() {
   2.572 +            return getOffset() + getLength();
   2.573 +        }
   2.574 +
   2.575 +        public boolean isCollapsed() {
   2.576 +            return (fold != null) ? fold.isCollapsed() : collapsed;
   2.577 +        }
   2.578 +        
   2.579 +        public boolean hasFold() {
   2.580 +            return (fold != null);
   2.581 +        }
   2.582 +        
   2.583 +        public void setCollapsed(boolean collapsed) {
   2.584 +            this.collapsed = collapsed;
   2.585 +        }
   2.586 +        
   2.587 +        public boolean isSolitaire() {
   2.588 +            return (pairMark == null);
   2.589 +        }
   2.590 +        
   2.591 +        public void makeSolitaire(boolean forced, FoldHierarchyTransaction transaction) {
   2.592 +            if (!isSolitaire()) {
   2.593 +                if (isStartMark()) {
   2.594 +                    setEndMark(null, forced, transaction);
   2.595 +                } else { // end mark
   2.596 +                    getPairMark().setEndMark(null, forced, transaction);
   2.597 +                }
   2.598 +            }
   2.599 +        }
   2.600 +        
   2.601 +        public boolean isReleased() {
   2.602 +            return released;
   2.603 +        }
   2.604 +        
   2.605 +        /**
   2.606 +         * Release this mark and mark for update.
   2.607 +         */
   2.608 +        public void release(boolean forced, FoldHierarchyTransaction transaction) {
   2.609 +            if (!released) {
   2.610 +                makeSolitaire(forced, transaction);
   2.611 +                released = true;
   2.612 +                markUpdate(this);
   2.613 +            }
   2.614 +        }
   2.615 +        
   2.616 +        public FoldMarkInfo getPairMark() {
   2.617 +            return pairMark;
   2.618 +        }
   2.619 +        
   2.620 +        private void setPairMark(FoldMarkInfo pairMark) {
   2.621 +            this.pairMark = pairMark;
   2.622 +        }
   2.623 +
   2.624 +        public void setEndMark(FoldMarkInfo endMark, boolean forced,
   2.625 +        FoldHierarchyTransaction transaction) {
   2.626 +            if (!isStartMark()) {
   2.627 +                throw new IllegalStateException("Not start mark"); // NOI18N
   2.628 +            }
   2.629 +            if (pairMark == endMark) {
   2.630 +                return;
   2.631 +            }
   2.632 +            
   2.633 +            if (pairMark != null) { // is currently paired to an end mark
   2.634 +                releaseFold(forced, transaction);
   2.635 +                pairMark.setPairMark(null);
   2.636 +            }
   2.637 +
   2.638 +            pairMark = endMark;
   2.639 +            if (endMark != null) {
   2.640 +                if (!endMark.isSolitaire()) { // make solitaire first
   2.641 +                    endMark.makeSolitaire(false, transaction); // not forced here
   2.642 +                }
   2.643 +                endMark.setPairMark(this);
   2.644 +                endMark.setParentMark(this.getParentMark());
   2.645 +                ensureFoldExists(transaction);
   2.646 +            }
   2.647 +        }
   2.648 +        
   2.649 +        public FoldMarkInfo getParentMark() {
   2.650 +            return parentMark;
   2.651 +        }
   2.652 +        
   2.653 +        public void setParentMark(FoldMarkInfo parentMark) {
   2.654 +            this.parentMark = parentMark;
   2.655 +        }
   2.656 +        
   2.657 +        private void releaseFold(boolean forced, FoldHierarchyTransaction transaction) {
   2.658 +            if (isSolitaire() || !isStartMark()) {
   2.659 +               throw new IllegalStateException();
   2.660 +            }
   2.661 +
   2.662 +            if (fold != null) {
   2.663 +                setCollapsed(fold.isCollapsed()); // serialize the collapsed info
   2.664 +                if (!forced) {
   2.665 +                    getOperation().removeFromHierarchy(fold, transaction);
   2.666 +                }
   2.667 +                fold = null;
   2.668 +            }
   2.669 +        }
   2.670 +
   2.671 +        public Fold getFold() {
   2.672 +            if (isSolitaire()) {
   2.673 +                return null;
   2.674 +            }
   2.675 +            if (!isStartMark()) {
   2.676 +                return pairMark.getFold();
   2.677 +            }
   2.678 +            return fold;
   2.679 +        }
   2.680 +        
   2.681 +        public void ensureFoldExists(FoldHierarchyTransaction transaction) {
   2.682 +            if (isSolitaire() || !isStartMark()) {
   2.683 +                throw new IllegalStateException();
   2.684 +            }
   2.685 +
   2.686 +            if (fold == null) {
   2.687 +                try {
   2.688 +                    if (!startMark) {
   2.689 +                        throw new IllegalStateException("Not start mark: " + this); // NOI18N
   2.690 +                    }
   2.691 +                    if (pairMark == null) {
   2.692 +                        throw new IllegalStateException("No pairMark for mark:" + this); // NOI18N
   2.693 +                    }
   2.694 +                    int startOffset = getOffset();
   2.695 +                    int startGuardedLength = getLength();
   2.696 +                    int endGuardedLength = pairMark.getLength();
   2.697 +                    int endOffset = pairMark.getOffset() + endGuardedLength;
   2.698 +                    fold = getOperation().addToHierarchy(
   2.699 +                        CUSTOM_FOLD_TYPE, getDescription(), collapsed,
   2.700 +                        startOffset, endOffset,
   2.701 +                        startGuardedLength, endGuardedLength,
   2.702 +                        this,
   2.703 +                        transaction
   2.704 +                    );
   2.705 +                } catch (BadLocationException e) {
   2.706 +                    LOG.log(Level.WARNING, null, e);
   2.707 +                }
   2.708 +            }
   2.709 +        }
   2.710 +        
   2.711 +        public @Override String toString() {
   2.712 +            StringBuffer sb = new StringBuffer();
   2.713 +            sb.append(isStartMark() ? 'S' : 'E');  // NOI18N
   2.714 +            
   2.715 +            // Check whether this mark (or its pair) has fold
   2.716 +            if (hasFold() || (!isSolitaire() && getPairMark().hasFold())) {
   2.717 +                sb.append("F"); // NOI18N
   2.718 +                
   2.719 +                // Check fold's status
   2.720 +                if (isStartMark() && (isSolitaire()
   2.721 +                        || getOffset() != fold.getStartOffset()
   2.722 +                        || getPairMark().getEndOffset() != fold.getEndOffset())
   2.723 +                ) {
   2.724 +                    sb.append("!!<"); // NOI18N
   2.725 +                    sb.append(fold.getStartOffset());
   2.726 +                    sb.append(","); // NOI18N
   2.727 +                    sb.append(fold.getEndOffset());
   2.728 +                    sb.append(">!!"); // NOI18N
   2.729 +                }
   2.730 +            }
   2.731 +
   2.732 +            // Append mark's internal status
   2.733 +            sb.append(" ("); // NOI18N
   2.734 +            sb.append("o="); // NOI18N
   2.735 +            sb.append(pos.getOffset());
   2.736 +            sb.append(", l="); // NOI18N
   2.737 +            sb.append(length);
   2.738 +            sb.append(", d='"); // NOI18N
   2.739 +            sb.append(description);
   2.740 +            sb.append('\'');
   2.741 +            if (getPairMark() != null) {
   2.742 +                sb.append(", <->"); // NOI18N
   2.743 +                sb.append(getPairMark().getOffset());
   2.744 +            }
   2.745 +            if (getParentMark() != null) {
   2.746 +                sb.append(", ^"); // NOI18N
   2.747 +                sb.append(getParentMark().getOffset());
   2.748 +            }
   2.749 +            sb.append(')');
   2.750 +            
   2.751 +            return sb.toString();
   2.752 +        }
   2.753 +
   2.754 +    }
   2.755 +        
   2.756 +    public static final class Factory implements FoldManagerFactory {
   2.757 +
   2.758 +        public FoldManager createFoldManager() {
   2.759 +            return new CustomFoldManager();
   2.760 +        }
   2.761 +    }
   2.762 +}
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/PLSQL/Folding/src/org/netbeans/modules/plsql/fold/NewPlsqlFoldManager.java	Mon Mar 11 15:26:32 2013 +0100
     3.3 @@ -0,0 +1,1443 @@
     3.4 +/*
     3.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3.6 + *
     3.7 + * Copyright 2011 Oracle and/or its affiliates. All rights reserved.
     3.8 + *
     3.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    3.10 + * Other names may be trademarks of their respective owners.
    3.11 + *
    3.12 + * The contents of this file are subject to the terms of either the GNU
    3.13 + * General Public License Version 2 only ("GPL") or the Common
    3.14 + * Development and Distribution License("CDDL") (collectively, the
    3.15 + * "License"). You may not use this file except in compliance with the
    3.16 + * License. You can obtain a copy of the License at
    3.17 + * http://www.netbeans.org/cddl-gplv2.html
    3.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    3.19 + * specific language governing permissions and limitations under the
    3.20 + * License.  When distributing the software, include this License Header
    3.21 + * Notice in each file and include the License file at
    3.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    3.23 + * particular file as subject to the "Classpath" exception as provided
    3.24 + * by Oracle in the GPL Version 2 section of the License file that
    3.25 + * accompanied this code. If applicable, add the following below the
    3.26 + * License Header, with the fields enclosed by brackets [] replaced by
    3.27 + * your own identifying information:
    3.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    3.29 + *
    3.30 + * If you wish your version of this file to be governed by only the CDDL
    3.31 + * or only the GPL Version 2, indicate your decision by adding
    3.32 + * "[Contributor] elects to include this software in this distribution
    3.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    3.34 + * single choice of license, a recipient has the option to distribute
    3.35 + * your version of this file under either the CDDL, the GPL Version 2 or
    3.36 + * to extend the choice of license to its licensees as provided above.
    3.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    3.38 + * Version 2 license, then the option applies only if the new code is
    3.39 + * made subject to such option by the copyright holder.
    3.40 + *
    3.41 + * Contributor(s):
    3.42 + *
    3.43 + * Portions Copyrighted 2011 Sun Microsystems, Inc.
    3.44 + */
    3.45 +package org.netbeans.modules.plsql.fold;
    3.46 +
    3.47 +import java.util.ArrayList;
    3.48 +import java.util.Collections;
    3.49 +import java.util.Comparator;
    3.50 +import java.util.HashMap;
    3.51 +import java.util.List;
    3.52 +import java.util.Map;
    3.53 +import java.util.Observable;
    3.54 +import java.util.Observer;
    3.55 +import java.util.logging.Level;
    3.56 +import java.util.logging.Logger;
    3.57 +import java.util.regex.Pattern;
    3.58 +import javax.swing.event.DocumentEvent;
    3.59 +import javax.swing.text.BadLocationException;
    3.60 +import javax.swing.text.Document;
    3.61 +import javax.swing.text.Position;
    3.62 +import org.netbeans.api.editor.fold.Fold;
    3.63 +import org.netbeans.api.editor.fold.FoldHierarchy;
    3.64 +import org.netbeans.api.editor.fold.FoldType;
    3.65 +import org.netbeans.editor.BaseDocument;
    3.66 +import org.netbeans.modules.plsql.lexer.PlsqlBlock;
    3.67 +import org.netbeans.modules.plsql.lexer.PlsqlBlockFactory;
    3.68 +import org.netbeans.modules.plsql.lexer.PlsqlBlockType;
    3.69 +import org.netbeans.modules.plsqlsupport.options.OptionsUtilities;
    3.70 +import org.netbeans.spi.editor.fold.FoldHierarchyTransaction;
    3.71 +import org.netbeans.spi.editor.fold.FoldManager;
    3.72 +import org.netbeans.spi.editor.fold.FoldOperation;
    3.73 +import org.openide.util.Lookup;
    3.74 +import org.openide.util.RequestProcessor;
    3.75 +
    3.76 +/**
    3.77 + *
    3.78 + * @author chrlse
    3.79 + */
    3.80 +public class NewPlsqlFoldManager implements FoldManager, Runnable, Observer {
    3.81 +
    3.82 +   private static final Logger LOG = Logger.getLogger(NewPlsqlFoldManager.class.getName());
    3.83 +//   public static final FoldType CUSTOM_FOLD_TYPE = new FoldType("custom-fold"); // NOI18N
    3.84 +   private FoldOperation operation;
    3.85 +   private Document doc;
    3.86 +   private org.netbeans.editor.GapObjectArray markArray = new org.netbeans.editor.GapObjectArray();
    3.87 +   private int minUpdateMarkOffset = Integer.MAX_VALUE;
    3.88 +   private int maxUpdateMarkOffset = -1;
    3.89 +   private List<Fold> removedFoldList;
    3.90 +   private Map<String, Boolean> customFoldId = new HashMap<String, Boolean>();
    3.91 +   private static final RequestProcessor RP = new RequestProcessor(NewPlsqlFoldManager.class.getName(),
    3.92 +           1, false, false);
    3.93 +   private final RequestProcessor.Task task = RP.create(this);
    3.94 +//   private NewPlsqlFoldManager observer;
    3.95 +   private boolean initial = true;
    3.96 +
    3.97 +   @Override
    3.98 +   public void init(FoldOperation operation) {
    3.99 +      this.operation = operation;
   3.100 +      if (LOG.isLoggable(Level.FINE)) {
   3.101 +         LOG.log(Level.FINE, "Initialized: {0}", System.identityHashCode(this));
   3.102 +      }
   3.103 +   }
   3.104 +
   3.105 +   private FoldOperation getOperation() {
   3.106 +      return operation;
   3.107 +   }
   3.108 +
   3.109 +   @Override
   3.110 +   public void initFolds(FoldHierarchyTransaction transaction) {
   3.111 +      doc = getOperation().getHierarchy().getComponent().getDocument();
   3.112 +      final PlsqlBlockFactory blockFactory = getBlockFactory();
   3.113 +      if (blockFactory != null) {
   3.114 +//         if (observer != null) {
   3.115 +//            blockFactory.deleteObserver(observer);
   3.116 +//         }
   3.117 +         blockFactory.addObserver(this);
   3.118 +//         observer = this;
   3.119 +      }
   3.120 +      task.schedule(300);
   3.121 +   }
   3.122 +
   3.123 +   //XXX: seems NewPlsqlFoldManager needs to listen to PlsqlBlockFactory changes, instead of directly to document through FoldManager interface 
   3.124 +   @Override
   3.125 +   public void insertUpdate(DocumentEvent evt, FoldHierarchyTransaction transaction) {
   3.126 +      processRemovedFolds(transaction);
   3.127 +//      myRemoveFolds(transaction);
   3.128 +//      task.schedule(300);
   3.129 +   }
   3.130 +
   3.131 +   @Override
   3.132 +   public void removeUpdate(DocumentEvent evt, FoldHierarchyTransaction transaction) {
   3.133 +      processRemovedFolds(transaction);
   3.134 +//      myRemoveFolds(transaction);
   3.135 +      removeAffectedMarks(evt, transaction);
   3.136 +//      task.schedule(300);
   3.137 +   }
   3.138 +
   3.139 +   @Override
   3.140 +   public void changedUpdate(DocumentEvent evt, FoldHierarchyTransaction transaction) {
   3.141 +   }
   3.142 +
   3.143 +   @Override
   3.144 +   public void removeEmptyNotify(Fold emptyFold) {
   3.145 +      removeFoldNotify(emptyFold);
   3.146 +   }
   3.147 +
   3.148 +   @Override
   3.149 +   public void removeDamagedNotify(Fold damagedFold) {
   3.150 +      removeFoldNotify(damagedFold);
   3.151 +   }
   3.152 +
   3.153 +   @Override
   3.154 +   public void expandNotify(Fold expandedFold) {
   3.155 +   }
   3.156 +
   3.157 +   @Override
   3.158 +   public void release() {
   3.159 +      if (LOG.isLoggable(Level.FINE)) {
   3.160 +         LOG.log(Level.FINE, "Released: {0}", System.identityHashCode(this));
   3.161 +      }
   3.162 +   }
   3.163 +
   3.164 +   @Override
   3.165 +   public void update(Observable o, Object arg) {
   3.166 +      task.schedule(300);
   3.167 +   }
   3.168 +
   3.169 +   @Override
   3.170 +   public void run() {
   3.171 +      if (operation.isReleased()) {
   3.172 +         if (LOG.isLoggable(Level.FINE)) {
   3.173 +            LOG.log(Level.FINE, "Update skipped, already released: {0}", System.identityHashCode(this));
   3.174 +         }
   3.175 +         return;
   3.176 +      }
   3.177 +      ((BaseDocument) doc).readLock();
   3.178 +      try {
   3.179 +
   3.180 +         final PlsqlBlockFactory blockFactory = getBlockFactory();
   3.181 +         if (blockFactory != null) {
   3.182 +            FoldHierarchy hierarchy = getOperation().getHierarchy();
   3.183 +            hierarchy.lock();
   3.184 +            try {
   3.185 +               if (operation.isReleased()) {
   3.186 +                  if (LOG.isLoggable(Level.FINE)) {
   3.187 +                     LOG.log(Level.FINE, "Update skipped, already released: {0}", System.identityHashCode(this));
   3.188 +                  }
   3.189 +                  return;
   3.190 +               }
   3.191 +               if (LOG.isLoggable(Level.FINE)) {
   3.192 +                  LOG.log(Level.FINE, "Updating: {0}", System.identityHashCode(this));
   3.193 +               }
   3.194 +               FoldHierarchyTransaction transaction = getOperation().openTransaction();
   3.195 +               try {
   3.196 +                  //Initialize document hierarchy
   3.197 +                  blockFactory.initHierarchy(doc);
   3.198 +                  //Add new blocks to the hierarchy
   3.199 +                  List<PlsqlBlock> blocks = blockFactory.getBlockHierarchy();
   3.200 +//                  List<PlsqlBlock> blocks = blockFactory.getNewBlocks();
   3.201 +//                  if (initial) {
   3.202 +//                     blocks = blockFactory.getBlockHierarchy();
   3.203 +//                     initial = false;
   3.204 +//                  }
   3.205 +//                  List<PlsqlBlock> removedBlocks = blockFactory.getRemovedBlocks();
   3.206 +
   3.207 +//                  final Fold root = hierarchy.getRootFold();
   3.208 +//                  final List<FoldInfo> collapsedFolds = new ArrayList<FoldInfo>();
   3.209 +//                  getCollapsedFolds(root, collapsedFolds);
   3.210 +//                  addFolds(newBlocks, transaction, collapsedFolds);
   3.211 +//                  addFolds(blockFactory.getCustomFolds(), transaction, collapsedFolds);
   3.212 +
   3.213 +                  updateFolds(blocks, transaction);
   3.214 +                  //Add custom fold blocks
   3.215 +                  updateFolds(blockFactory.getCustomFolds(), transaction);
   3.216 +               } finally {
   3.217 +                  transaction.commit();
   3.218 +               }
   3.219 +            } finally {
   3.220 +               hierarchy.unlock();
   3.221 +            }
   3.222 +         }
   3.223 +      } finally {
   3.224 +         ((BaseDocument) doc).readUnlock();
   3.225 +      }
   3.226 +   }
   3.227 +
   3.228 +   private void removeFoldNotify(Fold removedFold) {
   3.229 +      if (removedFoldList == null) {
   3.230 +         removedFoldList = new ArrayList<Fold>(3);
   3.231 +      }
   3.232 +      removedFoldList.add(removedFold);
   3.233 +   }
   3.234 +
   3.235 +   private void removeAffectedMarks(DocumentEvent evt, FoldHierarchyTransaction transaction) {
   3.236 +      int removeOffset = evt.getOffset();
   3.237 +      int markIndex = findMarkIndex(removeOffset);
   3.238 +      if (markIndex < getMarkCount()) {
   3.239 +         FoldMarkInfo mark;
   3.240 +         while (markIndex >= 0 && (mark = getMark(markIndex)).getOffset() == removeOffset) {
   3.241 +            mark.release(false, transaction);
   3.242 +            removeMark(markIndex);
   3.243 +            markIndex--;
   3.244 +         }
   3.245 +      }
   3.246 +   }
   3.247 +
   3.248 +   private void processRemovedFolds(FoldHierarchyTransaction transaction) {
   3.249 +      if (removedFoldList != null) {
   3.250 +         for (int i = removedFoldList.size() - 1; i >= 0; i--) {
   3.251 +            Fold removedFold = removedFoldList.get(i);
   3.252 +            FoldMarkInfo startMark = (FoldMarkInfo) getOperation().getExtraInfo(removedFold);
   3.253 +            if (startMark.getId() != null) {
   3.254 +               customFoldId.put(startMark.getId(), Boolean.valueOf(removedFold.isCollapsed())); // remember the last fold's state before remove
   3.255 +            }
   3.256 +            FoldMarkInfo endMark = startMark.getPairMark(); // get prior releasing
   3.257 +            if (getOperation().isStartDamaged(removedFold)) { // start mark area was damaged
   3.258 +               startMark.release(true, transaction); // forced remove
   3.259 +            }
   3.260 +            if (getOperation().isEndDamaged(removedFold)) {
   3.261 +               endMark.release(true, transaction);
   3.262 +            }
   3.263 +         }
   3.264 +      }
   3.265 +      removedFoldList = null;
   3.266 +   }
   3.267 +
   3.268 +   private void markUpdate(FoldMarkInfo mark) {
   3.269 +      markUpdate(mark.getOffset());
   3.270 +   }
   3.271 +
   3.272 +   private void markUpdate(int offset) {
   3.273 +      if (offset < minUpdateMarkOffset) {
   3.274 +         minUpdateMarkOffset = offset;
   3.275 +      }
   3.276 +      if (offset > maxUpdateMarkOffset) {
   3.277 +         maxUpdateMarkOffset = offset;
   3.278 +      }
   3.279 +   }
   3.280 +
   3.281 +   private FoldMarkInfo getMark(int index) {
   3.282 +      return (FoldMarkInfo) markArray.getItem(index);
   3.283 +   }
   3.284 +
   3.285 +   private int getMarkCount() {
   3.286 +      return markArray.getItemCount();
   3.287 +   }
   3.288 +
   3.289 +   private void removeMark(int index) {
   3.290 +      if (LOG.isLoggable(Level.FINE)) {
   3.291 +         LOG.log(Level.FINE, "Removing mark from ind={0}: {1}", new Object[]{index, getMark(index)}); // NOI18N
   3.292 +      }
   3.293 +      markArray.remove(index, 1);
   3.294 +   }
   3.295 +
   3.296 +   private void insertMark(int index, FoldMarkInfo mark) {
   3.297 +      markArray.insertItem(index, mark);
   3.298 +      if (LOG.isLoggable(Level.FINE)) {
   3.299 +         LOG.log(Level.FINE, "Inserted mark at ind={0}: {1}", new Object[]{index, mark}); // NOI18N
   3.300 +      }
   3.301 +   }
   3.302 +
   3.303 +   private int findMarkIndex(int offset) {
   3.304 +      int markCount = getMarkCount();
   3.305 +      int low = 0;
   3.306 +      int high = markCount - 1;
   3.307 +
   3.308 +      while (low <= high) {
   3.309 +         int mid = (low + high) / 2;
   3.310 +         int midMarkOffset = getMark(mid).getOffset();
   3.311 +
   3.312 +         if (midMarkOffset < offset) {
   3.313 +            low = mid + 1;
   3.314 +         } else if (midMarkOffset > offset) {
   3.315 +            high = mid - 1;
   3.316 +         } else {
   3.317 +            // mark starting exactly at the given offset found
   3.318 +            // If multiple -> find the one with highest index
   3.319 +            mid++;
   3.320 +            while (mid < markCount && getMark(mid).getOffset() == offset) {
   3.321 +               mid++;
   3.322 +            }
   3.323 +            mid--;
   3.324 +            return mid;
   3.325 +         }
   3.326 +      }
   3.327 +      return low; // return higher index (e.g. for insert)
   3.328 +   }
   3.329 +
   3.330 +   private List<FoldMarkInfo> getMarkList(List<PlsqlBlock> blocks) {
   3.331 +      List<FoldMarkInfo> markList = new ArrayList<FoldMarkInfo>();
   3.332 +//      for (PlsqlBlock block : blocks) {
   3.333 +//         Token token = blocks.token();
   3.334 +//         List<FoldMarkInfo> info;
   3.335 +      try {
   3.336 +         scanToken(markList, blocks);
   3.337 +      } catch (BadLocationException e) {
   3.338 +         LOG.log(Level.WARNING, null, e);
   3.339 +//            info = null;
   3.340 +      }
   3.341 +
   3.342 +//         if (info != null) {
   3.343 +//            if (markList == null) {
   3.344 +//               markList = new ArrayList<FoldMarkInfo>();
   3.345 +//            }
   3.346 +//            markList.addAll(info);
   3.347 +//         }
   3.348 +//      }
   3.349 +
   3.350 +      return markList;
   3.351 +   }
   3.352 +
   3.353 +   private void processTokenList(List<PlsqlBlock> blocks, FoldHierarchyTransaction transaction) {
   3.354 +      List<FoldMarkInfo> markList = getMarkList(blocks);
   3.355 +      int markListSize;
   3.356 +      if (markList != null && ((markListSize = markList.size()) > 0)) {
   3.357 +         // Find the index for insertion
   3.358 +         int offset = (markList.get(0)).getOffset();
   3.359 +         int arrayMarkIndex = findMarkIndex(offset);
   3.360 +         // Remember the corresponding mark in the array as well
   3.361 +         FoldMarkInfo arrayMark;
   3.362 +         int arrayMarkOffset;
   3.363 +         if (arrayMarkIndex < getMarkCount()) {
   3.364 +            arrayMark = getMark(arrayMarkIndex);
   3.365 +            arrayMarkOffset = arrayMark.getOffset();
   3.366 +         } else { // at last mark
   3.367 +            arrayMark = null;
   3.368 +            arrayMarkOffset = Integer.MAX_VALUE;
   3.369 +         }
   3.370 +
   3.371 +         for (int i = 0; i < markListSize; i++) {
   3.372 +            FoldMarkInfo listMark = markList.get(i);
   3.373 +            int listMarkOffset = listMark.getOffset();
   3.374 +            if (i == 0 || i == markListSize - 1) {
   3.375 +               // Update the update-offsets by the first and last marks in the list
   3.376 +               markUpdate(listMarkOffset);
   3.377 +            }
   3.378 +            while (listMarkOffset >= arrayMarkOffset) {
   3.379 +               if (listMarkOffset == arrayMarkOffset) {
   3.380 +                  // At the same offset - likely the same mark
   3.381 +                  //   -> retain the collapsed state
   3.382 +                  listMark.setCollapsed(arrayMark.isCollapsed());
   3.383 +               }
   3.384 +               if (!arrayMark.isReleased()) { // make sure that the mark is released
   3.385 +                  arrayMark.release(false, transaction);
   3.386 +               }
   3.387 +               removeMark(arrayMarkIndex);
   3.388 +               if (LOG.isLoggable(Level.FINE)) {
   3.389 +                  LOG.log(Level.FINE, "Removed dup mark from ind={0}: {1}", new Object[]{arrayMarkIndex, arrayMark}); // NOI18N
   3.390 +               }
   3.391 +               if (arrayMarkIndex < getMarkCount()) {
   3.392 +                  arrayMark = getMark(arrayMarkIndex);
   3.393 +                  arrayMarkOffset = arrayMark.getOffset();
   3.394 +               } else { // no more marks
   3.395 +                  arrayMark = null;
   3.396 +                  arrayMarkOffset = Integer.MAX_VALUE;
   3.397 +               }
   3.398 +            }
   3.399 +            // Insert the listmark
   3.400 +            insertMark(arrayMarkIndex, listMark);
   3.401 +            if (LOG.isLoggable(Level.FINE)) {
   3.402 +               LOG.log(Level.FINE, "Inserted mark at ind={0}: {1}", new Object[]{arrayMarkIndex, listMark}); // NOI18N
   3.403 +            }
   3.404 +            arrayMarkIndex++;
   3.405 +         }
   3.406 +      }
   3.407 +   }
   3.408 +
   3.409 +   private void updateFolds(List<PlsqlBlock> blocks, FoldHierarchyTransaction transaction) {
   3.410 +
   3.411 +      if (blocks != null && !blocks.isEmpty()) {
   3.412 +         processTokenList(blocks, transaction);
   3.413 +      }
   3.414 +
   3.415 +      if (maxUpdateMarkOffset == -1) { // no updates
   3.416 +         return;
   3.417 +      }
   3.418 +
   3.419 +      // Find the first mark to update and init the prevMark and parentMark prior the loop
   3.420 +      int index = findMarkIndex(minUpdateMarkOffset);
   3.421 +      FoldMarkInfo prevMark;
   3.422 +      FoldMarkInfo parentMark;
   3.423 +      if (index == 0) { // start from begining
   3.424 +         prevMark = null;
   3.425 +         parentMark = null;
   3.426 +      } else {
   3.427 +         prevMark = getMark(index - 1);
   3.428 +         parentMark = prevMark.getParentMark();
   3.429 +      }
   3.430 +
   3.431 +      // Iterate through the changed marks in the mark array 
   3.432 +      int markCount = getMarkCount();
   3.433 +      while (index < markCount) { // process the marks
   3.434 +         FoldMarkInfo mark = getMark(index);
   3.435 +
   3.436 +         // If the mark was released then it must be removed
   3.437 +         if (mark.isReleased()) {
   3.438 +            if (LOG.isLoggable(Level.FINE)) {
   3.439 +               LOG.log(Level.FINE, "Removing released mark at ind={0}: {1}", new Object[]{index, mark}); // NOI18N
   3.440 +            }
   3.441 +            removeMark(index);
   3.442 +            markCount--;
   3.443 +            continue;
   3.444 +         }
   3.445 +
   3.446 +         // Update mark's status (folds, parentMark etc.)
   3.447 +         if (mark.isStartMark()) { // starting a new fold
   3.448 +            if (prevMark == null || prevMark.isStartMark()) { // new level
   3.449 +               mark.setParentMark(prevMark); // prevMark == null means root level
   3.450 +               parentMark = prevMark;
   3.451 +
   3.452 +            } // same level => parent to the parent of the prevMark
   3.453 +
   3.454 +         } else { // end mark
   3.455 +            if (prevMark != null) {
   3.456 +               if (prevMark.isStartMark()) { // closing nearest fold
   3.457 +                  prevMark.setEndMark(mark, false, transaction);
   3.458 +
   3.459 +               } else { // prevMark is end mark - closing its parent fold
   3.460 +                  if (parentMark != null) {
   3.461 +                     // mark's parent gets set as well
   3.462 +                     parentMark.setEndMark(mark, false, transaction);
   3.463 +                     parentMark = parentMark.getParentMark();
   3.464 +
   3.465 +                  } else { // prevMark's parentMark is null (top level)
   3.466 +                     mark.makeSolitaire(false, transaction);
   3.467 +                  }
   3.468 +               }
   3.469 +
   3.470 +            } else { // prevMark is null
   3.471 +               mark.makeSolitaire(false, transaction);
   3.472 +            }
   3.473 +         }
   3.474 +
   3.475 +         // Set parent mark of the mark
   3.476 +         mark.setParentMark(parentMark);
   3.477 +
   3.478 +
   3.479 +         prevMark = mark;
   3.480 +         index++;
   3.481 +      }
   3.482 +
   3.483 +      minUpdateMarkOffset = Integer.MAX_VALUE;
   3.484 +      maxUpdateMarkOffset = -1;
   3.485 +
   3.486 +      if (LOG.isLoggable(Level.FINEST)) {
   3.487 +         LOG.log(Level.FINEST, "MARKS DUMP:\n{0}", this); //NOI18N
   3.488 +      }
   3.489 +   }
   3.490 +
   3.491 +   public @Override
   3.492 +   String toString() {
   3.493 +      StringBuffer sb = new StringBuffer();
   3.494 +      int markCount = getMarkCount();
   3.495 +      int markCountDigitCount = Integer.toString(markCount).length();
   3.496 +      for (int i = 0; i < markCount; i++) {
   3.497 +         sb.append("["); // NOI18N
   3.498 +         String iStr = Integer.toString(i);
   3.499 +         appendSpaces(sb, markCountDigitCount - iStr.length());
   3.500 +         sb.append(iStr);
   3.501 +         sb.append("]:"); // NOI18N
   3.502 +         FoldMarkInfo mark = getMark(i);
   3.503 +
   3.504 +         // Add extra indent regarding the depth in hierarchy
   3.505 +         int indent = 0;
   3.506 +         FoldMarkInfo parentMark = mark.getParentMark();
   3.507 +         while (parentMark != null) {
   3.508 +            indent += 4;
   3.509 +            parentMark = parentMark.getParentMark();
   3.510 +         }
   3.511 +         appendSpaces(sb, indent);
   3.512 +
   3.513 +         sb.append(mark);
   3.514 +         sb.append('\n');
   3.515 +      }
   3.516 +      return sb.toString();
   3.517 +   }
   3.518 +
   3.519 +   private static void appendSpaces(StringBuffer sb, int spaces) {
   3.520 +      while (--spaces >= 0) {
   3.521 +         sb.append(' ');
   3.522 +      }
   3.523 +   }
   3.524 +   private static Pattern pattern = Pattern.compile(
   3.525 +           "(<\\s*editor-fold"
   3.526 +           + // id="x"[opt] defaultstate="y"[opt] desc="z"[opt] defaultstate="a"[opt]
   3.527 +           // id must be first, the rest of attributes in random order
   3.528 +           "(?:(?:\\s+id=\"(\\S*)\")?(?:\\s+defaultstate=\"(\\S*?)\")?(?:\\s+desc=\"([\\S \\t]*?)\")?(?:\\s+defaultstate=\"(\\S*?)\")?)"
   3.529 +           + "\\s*>)|(?:</\\s*editor-fold\\s*>)"); // NOI18N
   3.530 +
   3.531 +   private void scanToken(List<FoldMarkInfo> list, List<PlsqlBlock> blocks) throws BadLocationException {
   3.532 +      // ignore any token that is not comment
   3.533 +//      if (token.id().primaryCategory() != null && token.id().primaryCategory().startsWith("comment")) { //NOI18N
   3.534 +//         Matcher matcher = pattern.matcher(token.text());
   3.535 +//         if (matcher.find()) {
   3.536 +//            if (matcher.group(1) != null) { // fold's start mark found
   3.537 +//               boolean state;
   3.538 +//               if (matcher.group(3) != null) {
   3.539 +//                  state = "collapsed".equals(matcher.group(3)); // remember the defaultstate // NOI18N
   3.540 +//               } else {
   3.541 +//                  state = "collapsed".equals(matcher.group(5));
   3.542 +//               }
   3.543 +//
   3.544 +//               if (matcher.group(2) != null) { // fold's id exists
   3.545 +//                  Boolean collapsed = (Boolean) customFoldId.get(matcher.group(2));
   3.546 +//                  if (collapsed != null) {
   3.547 +//                     state = collapsed.booleanValue(); // fold's state is already known from the past
   3.548 +//                  } else {
   3.549 +//                     customFoldId.put(matcher.group(2), Boolean.valueOf(state));
   3.550 +//                  }
   3.551 +//               }
   3.552 +//               return new FoldMarkInfo(true, token.offset(null), matcher.end(0), matcher.group(2), state, matcher.group(4)); // NOI18N
   3.553 +//            } else { // fold's end mark found
   3.554 +//               return new FoldMarkInfo(false, token.offset(null), matcher.end(0), null, false, null);
   3.555 +//            }
   3.556 +//         }
   3.557 +//      }
   3.558 +      for (PlsqlBlock block : blocks) {
   3.559 +
   3.560 +         FoldType foldType = null;
   3.561 +         final PlsqlBlockType type = block.getType();
   3.562 +         String description = null;
   3.563 +
   3.564 +         if (!(type == PlsqlBlockType.COMMENT && doc.getText(block.getStartOffset(), block.getEndOffset() - block.getStartOffset()).indexOf("\n") == -1)) { // check for single line comments
   3.565 +            if (type == PlsqlBlockType.VIEW) {
   3.566 +               foldType = PlsqlFoldTypes.VIEW;
   3.567 +               description = block.getPrefix() + "VIEW " + block.getName();
   3.568 +            } else if (type == PlsqlBlockType.TABLE_COMMENT) {
   3.569 +               foldType = PlsqlFoldTypes.TABLECOMMENT;
   3.570 +               description = "COMMENT ON TABLE " + block.getName();
   3.571 +            } else if (type == PlsqlBlockType.COLUMN_COMMENT) {
   3.572 +               foldType = PlsqlFoldTypes.COLUMNCOMMENT;
   3.573 +               description = "COLUMN COMMENTS ON TABLE " + block.getName();
   3.574 +            } else if (type == PlsqlBlockType.COMMENT) {
   3.575 +               foldType = PlsqlFoldTypes.COMMENT;
   3.576 +               description = block.getName();
   3.577 +            } else if (type == PlsqlBlockType.PACKAGE) {
   3.578 +               foldType = PlsqlFoldTypes.PACKAGE;
   3.579 +               description = block.getPrefix() + "PACKAGE " + block.getName();
   3.580 +            } else if (type == PlsqlBlockType.PACKAGE_BODY) {
   3.581 +               foldType = PlsqlFoldTypes.PACKAGEBODY;
   3.582 +               description = block.getPrefix() + "PACKAGE BODY " + block.getName();
   3.583 +            } else if (type == PlsqlBlockType.PROCEDURE_IMPL) {
   3.584 +               foldType = PlsqlFoldTypes.PROCEDUREIMPL;
   3.585 +               description = block.getPrefix() + "PROCEDURE IMPLEMENTATION " + block.getName();
   3.586 +            } else if (type == PlsqlBlockType.FUNCTION_IMPL) {
   3.587 +               foldType = PlsqlFoldTypes.FUNCTIONIMPL;
   3.588 +               description = block.getPrefix() + "FUNCTION IMPLEMENTATION " + block.getName();
   3.589 +            } else if (type == PlsqlBlockType.PROCEDURE_DEF) {
   3.590 +               foldType = PlsqlFoldTypes.PROCEDUREDEF;
   3.591 +               description = "PROCEDURE DEFINITION " + block.getName();
   3.592 +            } else if (type == PlsqlBlockType.FUNCTION_DEF) {
   3.593 +               foldType = PlsqlFoldTypes.FUNCTIONDEF;
   3.594 +               description = "FUNCTION DEFINITION " + block.getName();
   3.595 +            } else if (type == PlsqlBlockType.DECLARE_END) {
   3.596 +               foldType = PlsqlFoldTypes.DECLAREEND;
   3.597 +               description = "DECLARE BLOCK";
   3.598 +            } else if (type == PlsqlBlockType.BEGIN_END) {
   3.599 +               foldType = PlsqlFoldTypes.BEGINEND;
   3.600 +               description = "BEGIN BLOCK";
   3.601 +            } else if (type == PlsqlBlockType.TRIGGER) {
   3.602 +               foldType = PlsqlFoldTypes.TRIGGER;
   3.603 +               description = block.getPrefix() + "TRIGGER " + block.getName();
   3.604 +            } else if (type == PlsqlBlockType.IF) {
   3.605 +               foldType = PlsqlFoldTypes.IF;
   3.606 +               description = block.getName();
   3.607 +            } else if (type == PlsqlBlockType.CASE) {
   3.608 +               foldType = PlsqlFoldTypes.CASE;
   3.609 +               description = block.getName();
   3.610 +            } else if (type == PlsqlBlockType.WHILE_LOOP) {
   3.611 +               foldType = PlsqlFoldTypes.WHILELOOP;
   3.612 +               description = "WHILE " + block.getName();
   3.613 +            } else if (type == PlsqlBlockType.FOR_LOOP) {
   3.614 +               foldType = PlsqlFoldTypes.FORLOOP;
   3.615 +               description = "FOR " + block.getName();
   3.616 +            } else if (type == PlsqlBlockType.LOOP) {
   3.617 +               foldType = PlsqlFoldTypes.LOOP;
   3.618 +               description = "LOOP ";
   3.619 +            } else if (type == PlsqlBlockType.CUSTOM_FOLD) {
   3.620 +               foldType = PlsqlFoldTypes.CUSTOM;
   3.621 +               description = block.getName();
   3.622 +            } else if (type == PlsqlBlockType.STATEMENT) {
   3.623 +               foldType = PlsqlFoldTypes.STATEMENT;
   3.624 +               description = block.getPrefix() + block.getName();
   3.625 +            } else if (type == PlsqlBlockType.CURSOR) {
   3.626 +               foldType = PlsqlFoldTypes.CURSOR;
   3.627 +               description = "CURSOR " + block.getName();
   3.628 +            } else if (type == PlsqlBlockType.JAVA_SOURCE) {
   3.629 +               foldType = PlsqlFoldTypes.JAVASOURCE;
   3.630 +               description = block.getPrefix() + "JAVA SOURCE";
   3.631 +            }
   3.632 +            final int length = block.getPrefix().length() + block.getName().length();
   3.633 +//         List<FoldMarkInfo> list = new ArrayList<FoldMarkInfo>();
   3.634 +            //XXX: find out what offsets are needed (check CustomFoldManager when .php is run):
   3.635 +            list.add(new FoldMarkInfo(foldType, true, block.getStartOffset(), length, block.getName(), false, description));
   3.636 +            list.add(new FoldMarkInfo(foldType, false, block.getEndOffset(), 0, null, false, null));
   3.637 +            scanToken(list, block.getChildBlocks());
   3.638 +//         return list;
   3.639 +         }
   3.640 +      }
   3.641 +//      return null;
   3.642 +   }
   3.643 +
   3.644 +   private boolean myRemoveFolds(FoldHierarchyTransaction transaction) {
   3.645 +      final PlsqlBlockFactory blockFactory = getBlockFactory();
   3.646 +      if (blockFactory == null) {
   3.647 +         return true;
   3.648 +      }
   3.649 +      final FoldHierarchy fh = getOperation().getHierarchy();
   3.650 +      //Get folds from Block maker
   3.651 +      final Fold root = fh.getRootFold();
   3.652 +      final List<PlsqlBlock> oldBlocks = blockFactory.getRemovedBlocks();
   3.653 +      //Remove non existing blocks
   3.654 +      final int childCount = root.getFoldCount();
   3.655 +      if (!oldBlocks.isEmpty()) {
   3.656 +         for (int i = (childCount - 1); i >= 0; i--) {
   3.657 +            final Fold child = root.getFold(i);
   3.658 +            removeWithChildren(child, oldBlocks, transaction);
   3.659 +         }
   3.660 +      }
   3.661 +      return false;
   3.662 +   }
   3.663 +
   3.664 +   private final class FoldMarkInfo {
   3.665 +
   3.666 +      private boolean startMark;
   3.667 +      private Position pos;
   3.668 +      private int length;
   3.669 +      private String id;
   3.670 +      private boolean collapsed;
   3.671 +      private String description;
   3.672 +      /**
   3.673 +       * Matching pair mark used for fold construction
   3.674 +       */
   3.675 +      private FoldMarkInfo pairMark;
   3.676 +      /**
   3.677 +       * Parent mark defining nesting in the mark hierarchy.
   3.678 +       */
   3.679 +      private FoldMarkInfo parentMark;
   3.680 +      /**
   3.681 +       * Fold that corresponds to this mark (if it's start mark). It can be null if this mark is end mark or if it
   3.682 +       * currently does not have the fold assigned.
   3.683 +       */
   3.684 +      private Fold fold;
   3.685 +      private boolean released;
   3.686 +      private final FoldType foldType;
   3.687 +
   3.688 +      private FoldMarkInfo(FoldType foldType, boolean startMark, int offset,
   3.689 +              int length, String id, boolean collapsed, String description)
   3.690 +              throws BadLocationException {
   3.691 +         this.foldType = foldType;
   3.692 +         this.startMark = startMark;
   3.693 +         this.pos = doc.createPosition(offset);
   3.694 +         this.length = length;
   3.695 +         this.id = id;
   3.696 +         this.collapsed = collapsed;
   3.697 +         this.description = description;
   3.698 +      }
   3.699 +
   3.700 +      public String getId() {
   3.701 +         return id;
   3.702 +      }
   3.703 +
   3.704 +      public String getDescription() {
   3.705 +         return description;
   3.706 +      }
   3.707 +
   3.708 +      public boolean isStartMark() {
   3.709 +         return startMark;
   3.710 +      }
   3.711 +
   3.712 +      public int getLength() {
   3.713 +         return length;
   3.714 +      }
   3.715 +
   3.716 +      public int getOffset() {
   3.717 +         return pos.getOffset();
   3.718 +      }
   3.719 +
   3.720 +      public int getEndOffset() {
   3.721 +         return getOffset() + getLength();
   3.722 +      }
   3.723 +
   3.724 +      public boolean isCollapsed() {
   3.725 +         return (fold != null) ? fold.isCollapsed() : collapsed;
   3.726 +      }
   3.727 +
   3.728 +      public boolean hasFold() {
   3.729 +         return (fold != null);
   3.730 +      }
   3.731 +
   3.732 +      public void setCollapsed(boolean collapsed) {
   3.733 +         this.collapsed = collapsed;
   3.734 +      }
   3.735 +
   3.736 +      public boolean isSolitaire() {
   3.737 +         return (pairMark == null);
   3.738 +      }
   3.739 +
   3.740 +      public void makeSolitaire(boolean forced, FoldHierarchyTransaction transaction) {
   3.741 +         if (!isSolitaire()) {
   3.742 +            if (isStartMark()) {
   3.743 +               setEndMark(null, forced, transaction);
   3.744 +            } else { // end mark
   3.745 +               getPairMark().setEndMark(null, forced, transaction);
   3.746 +            }
   3.747 +         }
   3.748 +      }
   3.749 +
   3.750 +      public boolean isReleased() {
   3.751 +         return released;
   3.752 +      }
   3.753 +
   3.754 +      /**
   3.755 +       * Release this mark and mark for update.
   3.756 +       */
   3.757 +      public void release(boolean forced, FoldHierarchyTransaction transaction) {
   3.758 +         if (!released) {
   3.759 +            makeSolitaire(forced, transaction);
   3.760 +            released = true;
   3.761 +            markUpdate(this);
   3.762 +         }
   3.763 +      }
   3.764 +
   3.765 +      public FoldMarkInfo getPairMark() {
   3.766 +         return pairMark;
   3.767 +      }
   3.768 +
   3.769 +      private void setPairMark(FoldMarkInfo pairMark) {
   3.770 +         this.pairMark = pairMark;
   3.771 +      }
   3.772 +
   3.773 +      public void setEndMark(FoldMarkInfo endMark, boolean forced,
   3.774 +              FoldHierarchyTransaction transaction) {
   3.775 +         if (!isStartMark()) {
   3.776 +            throw new IllegalStateException("Not start mark"); // NOI18N
   3.777 +         }
   3.778 +         if (pairMark == endMark) {
   3.779 +            return;
   3.780 +         }
   3.781 +
   3.782 +         if (pairMark != null) { // is currently paired to an end mark
   3.783 +            releaseFold(forced, transaction);
   3.784 +            pairMark.setPairMark(null);
   3.785 +         }
   3.786 +
   3.787 +         pairMark = endMark;
   3.788 +         if (endMark != null) {
   3.789 +            if (!endMark.isSolitaire()) { // make solitaire first
   3.790 +               endMark.makeSolitaire(false, transaction); // not forced here
   3.791 +            }
   3.792 +            endMark.setPairMark(this);
   3.793 +            endMark.setParentMark(this.getParentMark());
   3.794 +            ensureFoldExists(transaction);
   3.795 +         }
   3.796 +      }
   3.797 +
   3.798 +      public FoldMarkInfo getParentMark() {
   3.799 +         return parentMark;
   3.800 +      }
   3.801 +
   3.802 +      public void setParentMark(FoldMarkInfo parentMark) {
   3.803 +         this.parentMark = parentMark;
   3.804 +      }
   3.805 +
   3.806 +      private void releaseFold(boolean forced, FoldHierarchyTransaction transaction) {
   3.807 +         if (isSolitaire() || !isStartMark()) {
   3.808 +            throw new IllegalStateException();
   3.809 +         }
   3.810 +
   3.811 +         if (fold != null) {
   3.812 +            setCollapsed(fold.isCollapsed()); // serialize the collapsed info
   3.813 +            if (!forced) {
   3.814 +               getOperation().removeFromHierarchy(fold, transaction);
   3.815 +            }
   3.816 +            fold = null;
   3.817 +         }
   3.818 +      }
   3.819 +
   3.820 +      public Fold getFold() {
   3.821 +         if (isSolitaire()) {
   3.822 +            return null;
   3.823 +         }
   3.824 +         if (!isStartMark()) {
   3.825 +            return pairMark.getFold();
   3.826 +         }
   3.827 +         return fold;
   3.828 +      }
   3.829 +
   3.830 +      public void ensureFoldExists(FoldHierarchyTransaction transaction) {
   3.831 +         if (isSolitaire() || !isStartMark()) {
   3.832 +            throw new IllegalStateException();
   3.833 +         }
   3.834 +
   3.835 +         if (fold == null) {
   3.836 +            try {
   3.837 +               if (!startMark) {
   3.838 +                  throw new IllegalStateException("Not start mark: " + this); // NOI18N
   3.839 +               }
   3.840 +               if (pairMark == null) {
   3.841 +                  throw new IllegalStateException("No pairMark for mark:" + this); // NOI18N
   3.842 +               }
   3.843 +               int startOffset = getOffset();
   3.844 +               int startGuardedLength = getLength();
   3.845 +               int endGuardedLength = pairMark.getLength();
   3.846 +               int endOffset = pairMark.getOffset() + endGuardedLength;
   3.847 +               fold = getOperation().addToHierarchy(
   3.848 +                       foldType, getDescription(), collapsed,
   3.849 +                       startOffset, endOffset,
   3.850 +                       startGuardedLength, endGuardedLength,
   3.851 +                       this,
   3.852 +                       transaction);
   3.853 +            } catch (BadLocationException e) {
   3.854 +               LOG.log(Level.WARNING, null, e);
   3.855 +            }
   3.856 +         }
   3.857 +      }
   3.858 +
   3.859 +      @Override
   3.860 +      public String toString() {
   3.861 +         StringBuilder sb = new StringBuilder();
   3.862 +         sb.append(isStartMark() ? 'S' : 'E');  // NOI18N
   3.863 +
   3.864 +         // Check whether this mark (or its pair) has fold
   3.865 +         if (hasFold() || (!isSolitaire() && getPairMark().hasFold())) {
   3.866 +            sb.append("F"); // NOI18N
   3.867 +
   3.868 +            // Check fold's status
   3.869 +            if (isStartMark() && (isSolitaire()
   3.870 +                    || getOffset() != fold.getStartOffset()
   3.871 +                    || getPairMark().getEndOffset() != fold.getEndOffset())) {
   3.872 +               sb.append("!!<"); // NOI18N
   3.873 +               sb.append(fold.getStartOffset());
   3.874 +               sb.append(","); // NOI18N
   3.875 +               sb.append(fold.getEndOffset());
   3.876 +               sb.append(">!!"); // NOI18N
   3.877 +            }
   3.878 +         }
   3.879 +
   3.880 +         // Append mark's internal status
   3.881 +         sb.append(" ("); // NOI18N
   3.882 +         sb.append("o="); // NOI18N
   3.883 +         sb.append(pos.getOffset());
   3.884 +         sb.append(", l="); // NOI18N
   3.885 +         sb.append(length);
   3.886 +         sb.append(", d='"); // NOI18N
   3.887 +         sb.append(description);
   3.888 +         sb.append('\'');
   3.889 +         if (getPairMark() != null) {
   3.890 +            sb.append(", <->"); // NOI18N
   3.891 +            sb.append(getPairMark().getOffset());
   3.892 +         }
   3.893 +         if (getParentMark() != null) {
   3.894 +            sb.append(", ^"); // NOI18N
   3.895 +            sb.append(getParentMark().getOffset());
   3.896 +         }
   3.897 +         sb.append(')');
   3.898 +
   3.899 +         return sb.toString();
   3.900 +      }
   3.901 +   }
   3.902 +//   @Override
   3.903 +//   public void initFolds(final FoldHierarchyTransaction foldHierarchyTransaction) {
   3.904 +//      final Document doc = getDocument();
   3.905 +//      if (!(doc instanceof BaseDocument)) {
   3.906 +//         return;
   3.907 +//      }
   3.908 +//
   3.909 +//      final PlsqlBlockFactory blockFactory = getBlockFactory();
   3.910 +//      if (blockFactory != null) {
   3.911 +//         try {
   3.912 +//
   3.913 +//            //Initialize document hierarchy
   3.914 +//            blockFactory.initHierarchy(doc);
   3.915 +//            //Add new blocks to the hierarchy
   3.916 +//            List<PlsqlBlock> newBlocks;
   3.917 +//            if (initial) {
   3.918 +//               newBlocks = blockFactory.getBlockHierarchy();
   3.919 +//               initial = false;
   3.920 +//            } else {
   3.921 +//               newBlocks = blockFactory.getNewBlocks();
   3.922 +//            }
   3.923 +//            addFolds(newBlocks, foldHierarchyTransaction, null);
   3.924 +//            //Add custom fold blocks
   3.925 +//            addFolds(blockFactory.getCustomFolds(), foldHierarchyTransaction, null);
   3.926 +//         } catch (BadLocationException ex) {
   3.927 +//            Exceptions.printStackTrace(ex);
   3.928 +//         }
   3.929 +//      }
   3.930 +//   }
   3.931 +//
   3.932 +
   3.933 +   /**
   3.934 +    * Add folds to the folder hierarchy (initial)
   3.935 +    *
   3.936 +    * @param blockHier
   3.937 +    * @param fhTransaction
   3.938 +    */
   3.939 +   private void addFolds(final List<PlsqlBlock> blockHier, final FoldHierarchyTransaction fhTransaction, final List<FoldInfo> collapsedFolds) {
   3.940 +      final int count = blockHier.size();
   3.941 +      for (int i = 0; i < count; i++) {
   3.942 +         try {
   3.943 +            final PlsqlBlock temp = blockHier.get(i);
   3.944 +            FoldType foldType = null;
   3.945 +            final PlsqlBlockType type = temp.getType();
   3.946 +            String description = "";
   3.947 +
   3.948 +            if (!(type == PlsqlBlockType.COMMENT && doc.getText(temp.getStartOffset(), temp.getEndOffset() - temp.getStartOffset()).indexOf("\n") == -1)) { // check for single line comments
   3.949 +               if (type == PlsqlBlockType.VIEW) {
   3.950 +                  foldType = PlsqlFoldTypes.VIEW;
   3.951 +                  description = temp.getPrefix() + "VIEW " + temp.getName();
   3.952 +               } else if (type == PlsqlBlockType.TABLE_COMMENT) {
   3.953 +                  foldType = PlsqlFoldTypes.TABLECOMMENT;
   3.954 +                  description = "COMMENT ON TABLE " + temp.getName();
   3.955 +               } else if (type == PlsqlBlockType.COLUMN_COMMENT) {
   3.956 +                  foldType = PlsqlFoldTypes.COLUMNCOMMENT;
   3.957 +                  description = "COLUMN COMMENTS ON TABLE " + temp.getName();
   3.958 +               } else if (type == PlsqlBlockType.COMMENT) {
   3.959 +                  foldType = PlsqlFoldTypes.COMMENT;
   3.960 +                  description = temp.getName();
   3.961 +               } else if (type == PlsqlBlockType.PACKAGE) {
   3.962 +                  foldType = PlsqlFoldTypes.PACKAGE;
   3.963 +                  description = temp.getPrefix() + "PACKAGE " + temp.getName();
   3.964 +               } else if (type == PlsqlBlockType.PACKAGE_BODY) {
   3.965 +                  foldType = PlsqlFoldTypes.PACKAGEBODY;
   3.966 +                  description = temp.getPrefix() + "PACKAGE BODY " + temp.getName();
   3.967 +               } else if (type == PlsqlBlockType.PROCEDURE_IMPL) {
   3.968 +                  foldType = PlsqlFoldTypes.PROCEDUREIMPL;
   3.969 +                  description = temp.getPrefix() + "PROCEDURE IMPLEMENTATION " + temp.getName();
   3.970 +               } else if (type == PlsqlBlockType.FUNCTION_IMPL) {
   3.971 +                  foldType = PlsqlFoldTypes.FUNCTIONIMPL;
   3.972 +                  description = temp.getPrefix() + "FUNCTION IMPLEMENTATION " + temp.getName();
   3.973 +               } else if (type == PlsqlBlockType.PROCEDURE_DEF) {
   3.974 +                  foldType = PlsqlFoldTypes.PROCEDUREDEF;
   3.975 +                  description = "PROCEDURE DEFINITION " + temp.getName();
   3.976 +               } else if (type == PlsqlBlockType.FUNCTION_DEF) {
   3.977 +                  foldType = PlsqlFoldTypes.FUNCTIONDEF;
   3.978 +                  description = "FUNCTION DEFINITION " + temp.getName();
   3.979 +               } else if (type == PlsqlBlockType.DECLARE_END) {
   3.980 +                  foldType = PlsqlFoldTypes.DECLAREEND;
   3.981 +                  description = "DECLARE BLOCK";
   3.982 +               } else if (type == PlsqlBlockType.BEGIN_END) {
   3.983 +                  foldType = PlsqlFoldTypes.BEGINEND;
   3.984 +                  description = "BEGIN BLOCK";
   3.985 +               } else if (type == PlsqlBlockType.TRIGGER) {
   3.986 +                  foldType = PlsqlFoldTypes.TRIGGER;
   3.987 +                  description = temp.getPrefix() + "TRIGGER " + temp.getName();
   3.988 +               } else if (type == PlsqlBlockType.IF) {
   3.989 +                  foldType = PlsqlFoldTypes.IF;
   3.990 +                  description = temp.getName();
   3.991 +               } else if (type == PlsqlBlockType.CASE) {
   3.992 +                  foldType = PlsqlFoldTypes.CASE;
   3.993 +                  description = temp.getName();
   3.994 +               } else if (type == PlsqlBlockType.WHILE_LOOP) {
   3.995 +                  foldType = PlsqlFoldTypes.WHILELOOP;
   3.996 +                  description = "WHILE " + temp.getName();
   3.997 +               } else if (type == PlsqlBlockType.FOR_LOOP) {
   3.998 +                  foldType = PlsqlFoldTypes.FORLOOP;
   3.999 +                  description = "FOR " + temp.getName();
  3.1000 +               } else if (type == PlsqlBlockType.LOOP) {
  3.1001 +                  foldType = PlsqlFoldTypes.LOOP;
  3.1002 +                  description = "LOOP ";
  3.1003 +               } else if (type == PlsqlBlockType.CUSTOM_FOLD) {
  3.1004 +                  foldType = PlsqlFoldTypes.CUSTOM;
  3.1005 +                  description = temp.getName();
  3.1006 +               } else if (type == PlsqlBlockType.STATEMENT) {
  3.1007 +                  foldType = PlsqlFoldTypes.STATEMENT;
  3.1008 +                  description = temp.getPrefix() + temp.getName();
  3.1009 +               } else if (type == PlsqlBlockType.CURSOR) {
  3.1010 +                  foldType = PlsqlFoldTypes.CURSOR;
  3.1011 +                  description = "CURSOR " + temp.getName();
  3.1012 +               } else if (type == PlsqlBlockType.JAVA_SOURCE) {
  3.1013 +                  foldType = PlsqlFoldTypes.JAVASOURCE;
  3.1014 +                  description = temp.getPrefix() + "JAVA SOURCE";
  3.1015 +               }
  3.1016 +
  3.1017 +               if (doc.getEndPosition().getOffset() >= temp.getEndOffset()) {
  3.1018 +                  operation.addToHierarchy(foldType, description, isCollapsed(temp, foldType, collapsedFolds),
  3.1019 +                          temp.getStartOffset(), temp.getEndOffset(), 0, 0, null, fhTransaction);
  3.1020 +
  3.1021 +                  //check for any child folds and add them also
  3.1022 +                  addFolds(temp.getChildBlocks(), fhTransaction, collapsedFolds);
  3.1023 +               }
  3.1024 +            }
  3.1025 +         } catch (BadLocationException ex) {
  3.1026 +            LOG.log(Level.FINE, "BadLocationException thrown in addFolds", ex);
  3.1027 +         }
  3.1028 +      }
  3.1029 +   }
  3.1030 +
  3.1031 +   private void getBlockMap(final Map<String, PlsqlBlock> blockMap, final List<PlsqlBlock> blockHierarchy) {
  3.1032 +      for (int i = 0; i < blockHierarchy.size(); i++) {
  3.1033 +         final PlsqlBlock temp = blockHierarchy.get(i);
  3.1034 +         blockMap.put(temp.getStartOffset() + "_" + temp.getEndOffset(), temp);
  3.1035 +
  3.1036 +         getBlockMap(blockMap, temp.getChildBlocks());
  3.1037 +      }
  3.1038 +   }
  3.1039 +
  3.1040 +   /**
  3.1041 +    * Method that will add collapsed folds (FoldInfo objects) in the fold hierarchy to the given list
  3.1042 +    *
  3.1043 +    * @param parent
  3.1044 +    * @param foldInfoLst
  3.1045 +    */
  3.1046 +   private void getCollapsedFolds(final Fold parent, final List<FoldInfo> foldInfoLst) {
  3.1047 +      if (parent.isCollapsed()) {
  3.1048 +         final FoldInfo tempInfo = new FoldInfo(parent.getStartOffset(), parent.getEndOffset(), parent.getType());
  3.1049 +         foldInfoLst.add(tempInfo);
  3.1050 +      }
  3.1051 +      final int count = parent.getFoldCount();
  3.1052 +      for (int i = 0; i < count; i++) {
  3.1053 +         final Fold temp = parent.getFold(i);
  3.1054 +         getCollapsedFolds(temp, foldInfoLst);
  3.1055 +      }
  3.1056 +   }
  3.1057 +
  3.1058 +   /**
  3.1059 +    * Method that will select and return the corresponding fold to parent from oldRoot fold hierarchy
  3.1060 +    *
  3.1061 +    * @param parent
  3.1062 +    * @param foldInfoLst
  3.1063 +    * @return
  3.1064 +    */
  3.1065 +   private boolean isCollapsed(final PlsqlBlock block, final FoldType foldType, final List<FoldInfo> foldInfoLst) {
  3.1066 +      if (foldInfoLst == null) {
  3.1067 +         if (OptionsUtilities.isPlSqlExpandFolds()) {
  3.1068 +            return false;
  3.1069 +         } else {
  3.1070 +            return foldType == PlsqlFoldTypes.COMMENT;
  3.1071 +         }
  3.1072 +      }
  3.1073 +      final int size = foldInfoLst.size();
  3.1074 +      for (int i = 0; i < size; i++) {
  3.1075 +         final FoldInfo temp = foldInfoLst.get(i);
  3.1076 +
  3.1077 +         if ((temp.foldType == foldType)
  3.1078 +                 && (temp.startOffset == block.getPreviousStart())
  3.1079 +                 && (temp.endOffset == block.getPreviousEnd())) {
  3.1080 +            return true;
  3.1081 +         }
  3.1082 +      }
  3.1083 +
  3.1084 +      return false;
  3.1085 +   }
  3.1086 +
  3.1087 +//   /**
  3.1088 +//    * Method that will update the folds based on the changes to the document
  3.1089 +//    *
  3.1090 +//    * @param fhTran
  3.1091 +//    */
  3.1092 +//   private synchronized void updateFolds(final FoldHierarchyTransaction fhTran) {
  3.1093 +//      try {
  3.1094 +//         final PlsqlBlockFactory blockFactory = getBlockFactory();
  3.1095 +//         if (blockFactory == null) {
  3.1096 +//            return;
  3.1097 +//         }
  3.1098 +//
  3.1099 +//         final FoldHierarchy fh = getOperation().getHierarchy();
  3.1100 +//         try {
  3.1101 +//            //lock the hierarchy
  3.1102 +//            fh.lock();
  3.1103 +//
  3.1104 +//            final Fold root = fh.getRootFold();
  3.1105 +//            final List<FoldInfo> collapsedFolds = new ArrayList<FoldInfo>();
  3.1106 +//            getCollapsedFolds(root, collapsedFolds);
  3.1107 +//
  3.1108 +//            //Get folds from Block maker
  3.1109 +//            final List<PlsqlBlock> oldBlocks = blockFactory.getRemovedBlocks();
  3.1110 +//
  3.1111 +//            //Remove non existing blocks
  3.1112 +//            final int childCount = root.getFoldCount();
  3.1113 +//            if (!oldBlocks.isEmpty()) {
  3.1114 +//               for (int i = (childCount - 1); i >= 0; i--) {
  3.1115 +//                  final Fold child = root.getFold(i);
  3.1116 +//                  removeWithChildren(child, oldBlocks, fhTran);
  3.1117 +//               }
  3.1118 +//            }
  3.1119 +//
  3.1120 +//            //Add new blocks to the hierarchy
  3.1121 +//            final List<PlsqlBlock> newBlocks = blockFactory.getNewBlocks();
  3.1122 +//            addFolds(newBlocks, fhTran, collapsedFolds);
  3.1123 +//            //Add custom folds
  3.1124 +//            addFolds(blockFactory.getCustomFolds(), fhTran, collapsedFolds);
  3.1125 +//
  3.1126 +//            if (blockFactory.isAliasesChanged()) {
  3.1127 +//               final Map<String, PlsqlBlock> blockMap = new HashMap<String, PlsqlBlock>();
  3.1128 +//               getBlockMap(blockMap, blockFactory.getBlockHierarchy());
  3.1129 +//               final int rootChildren = root.getFoldCount();
  3.1130 +//               for (int i = (rootChildren - 1); i >= 0; i--) {
  3.1131 +//                  final Fold child = root.getFold(i);
  3.1132 +//                  changeDescriptions(child, blockMap, fhTran);
  3.1133 +//               }
  3.1134 +//            }
  3.1135 +//
  3.1136 +//            //compareFoldHierarchies(root, collapsedFolds, fhTran);
  3.1137 +//         } finally {
  3.1138 +//            fh.unlock();
  3.1139 +//         }
  3.1140 +//      } catch (Exception e) {
  3.1141 +//         ErrorManager.getDefault().notify(e);
  3.1142 +//      }
  3.1143 +//   }
  3.1144 +//
  3.1145 +//   /**
  3.1146 +//    * Method that will change the fold descriptions if changed
  3.1147 +//    *
  3.1148 +//    * @param parent
  3.1149 +//    * @param blockMap
  3.1150 +//    * @param fhTran
  3.1151 +//    */
  3.1152 +//   private void changeDescriptions(final Fold parent, final Map<String, PlsqlBlock> blockMap, final FoldHierarchyTransaction fhTran) throws BadLocationException {
  3.1153 +//      final PlsqlBlock block = getCorrespondingBlock(parent, blockMap);
  3.1154 +//
  3.1155 +//      //check whether block name is an alias
  3.1156 +//      if ((block != null) && (!block.getAlias().equals(""))) {  //Aliases wont be there for COMMENT & DECLARE
  3.1157 +//         final String description = getFoldDescription(block);
  3.1158 +//         //check whether description is different
  3.1159 +//         if (!parent.getDescription().equals(description)) {
  3.1160 +//            operation.removeFromHierarchy(parent, fhTran);
  3.1161 +//            operation.addToHierarchy(parent.getType(), description, parent.isCollapsed(),
  3.1162 +//                    block.getStartOffset(), block.getEndOffset(), 0, 0, null, fhTran);
  3.1163 +//         }
  3.1164 +//      }
  3.1165 +//
  3.1166 +//      final int childCount = parent.getFoldCount();
  3.1167 +//      for (int i = (childCount - 1); i >= 0; i--) {
  3.1168 +//         final Fold child = parent.getFold(i);
  3.1169 +//         //Ignore types that cannot have the name as an alias
  3.1170 +//         if ((child.getType() != PlsqlFoldTypes.BEGINEND)
  3.1171 +//                 && (child.getType() != PlsqlFoldTypes.COMMENT)
  3.1172 +//                 && (child.getType() != PlsqlFoldTypes.DECLAREEND)
  3.1173 +//                 && (child.getType() != PlsqlFoldTypes.IF)
  3.1174 +//                 && (child.getType() != PlsqlFoldTypes.CUSTOM)
  3.1175 +//                 && (child.getType() != PlsqlFoldTypes.FORLOOP)
  3.1176 +//                 && (child.getType() != PlsqlFoldTypes.LOOP)
  3.1177 +//                 && (child.getType() != PlsqlFoldTypes.WHILELOOP)) {
  3.1178 +//
  3.1179 +//            changeDescriptions(child, blockMap, fhTran);
  3.1180 +//         }
  3.1181 +//      }
  3.1182 +//   }
  3.1183 +//
  3.1184 +   /**
  3.1185 +    * Get the matching block to the fold. Consider the type, start & end offset
  3.1186 +    *
  3.1187 +    * @param fold
  3.1188 +    * @param blockMap
  3.1189 +    * @return
  3.1190 +    */
  3.1191 +   private PlsqlBlock getCorrespondingBlock(final Fold fold, final Map<String, PlsqlBlock> blockMap) {
  3.1192 +
  3.1193 +      final PlsqlBlock temp = blockMap.get(fold.getStartOffset() + "_" + fold.getEndOffset());
  3.1194 +      if (temp != null && isCorrespondingType(temp.getType(), fold.getType())) {
  3.1195 +         return temp;
  3.1196 +      }
  3.1197 +
  3.1198 +      return null;
  3.1199 +   }
  3.1200 +
  3.1201 +   /**
  3.1202 +    * Method that will check whether the block type and the fold type are matching
  3.1203 +    *
  3.1204 +    * @param blockType
  3.1205 +    * @param foldType
  3.1206 +    * @return
  3.1207 +    */
  3.1208 +   private boolean isCorrespondingType(final PlsqlBlockType blockType, final FoldType foldType) {
  3.1209 +      switch (blockType) {
  3.1210 +         case VIEW:
  3.1211 +            return foldType == PlsqlFoldTypes.VIEW;
  3.1212 +         case TABLE_COMMENT:
  3.1213 +            return foldType == PlsqlFoldTypes.TABLECOMMENT;
  3.1214 +         case COLUMN_COMMENT:
  3.1215 +            return foldType == PlsqlFoldTypes.COLUMNCOMMENT;
  3.1216 +         case COMMENT:
  3.1217 +            return foldType == PlsqlFoldTypes.COMMENT;
  3.1218 +         case PACKAGE:
  3.1219 +            return foldType == PlsqlFoldTypes.PACKAGE;
  3.1220 +         case PACKAGE_BODY:
  3.1221 +            return foldType == PlsqlFoldTypes.PACKAGEBODY;
  3.1222 +         case PROCEDURE_IMPL:
  3.1223 +            return foldType == PlsqlFoldTypes.PROCEDUREIMPL;
  3.1224 +         case FUNCTION_IMPL:
  3.1225 +            return foldType == PlsqlFoldTypes.FUNCTIONIMPL;
  3.1226 +         case PROCEDURE_DEF:
  3.1227 +            return foldType == PlsqlFoldTypes.PROCEDUREDEF;
  3.1228 +         case FUNCTION_DEF:
  3.1229 +            return foldType == PlsqlFoldTypes.FUNCTIONDEF;
  3.1230 +         case DECLARE_END:
  3.1231 +            return foldType == PlsqlFoldTypes.DECLAREEND;
  3.1232 +         case BEGIN_END:
  3.1233 +            return foldType == PlsqlFoldTypes.BEGINEND;
  3.1234 +         case TRIGGER:
  3.1235 +            return foldType == PlsqlFoldTypes.TRIGGER;
  3.1236 +         case IF:
  3.1237 +            return foldType == PlsqlFoldTypes.IF;
  3.1238 +         case CASE:
  3.1239 +            return foldType == PlsqlFoldTypes.CASE;
  3.1240 +         case WHILE_LOOP:
  3.1241 +            return foldType == PlsqlFoldTypes.WHILELOOP;
  3.1242 +         case FOR_LOOP:
  3.1243 +            return foldType == PlsqlFoldTypes.FORLOOP;
  3.1244 +         case LOOP:
  3.1245 +            return foldType == PlsqlFoldTypes.LOOP;
  3.1246 +         case CUSTOM_FOLD:
  3.1247 +            return foldType == PlsqlFoldTypes.CUSTOM;
  3.1248 +         case STATEMENT:
  3.1249 +            return foldType == PlsqlFoldTypes.STATEMENT;
  3.1250 +         case CURSOR:
  3.1251 +            return foldType == PlsqlFoldTypes.CURSOR;
  3.1252 +         case JAVA_SOURCE:
  3.1253 +            return foldType == PlsqlFoldTypes.JAVASOURCE;
  3.1254 +         default:
  3.1255 +            return false;
  3.1256 +      }
  3.1257 +   }
  3.1258 +
  3.1259 +   /**
  3.1260 +    * Method that will return the fold description given the Plsql block Used when changing the descriptions only.
  3.1261 +    *
  3.1262 +    * @param block
  3.1263 +    * @return
  3.1264 +    */
  3.1265 +   private String getFoldDescription(final PlsqlBlock block) {
  3.1266 +      switch (block.getType()) {
  3.1267 +         case VIEW:
  3.1268 +            return block.getPrefix() + "VIEW " + block.getName();
  3.1269 +         case TABLE_COMMENT:
  3.1270 +            return "COMMENT ON TABLE " + block.getName();
  3.1271 +         case COLUMN_COMMENT:
  3.1272 +            return "COLUMN COMMENTS ON TABLE " + block.getName();
  3.1273 +         case COMMENT:
  3.1274 +            return block.getName();
  3.1275 +         case PACKAGE:
  3.1276 +            return block.getPrefix() + "PACKAGE " + block.getName();
  3.1277 +         case PACKAGE_BODY:
  3.1278 +            return block.getPrefix() + "PACKAGE BODY " + block.getName();
  3.1279 +         case PROCEDURE_IMPL:
  3.1280 +            return block.getPrefix() + "PROCEDURE IMPLEMENTATION " + block.getName();
  3.1281 +         case FUNCTION_IMPL:
  3.1282 +            return block.getPrefix() + "FUNCTION IMPLEMENTATION " + block.getName();
  3.1283 +         case PROCEDURE_DEF:
  3.1284 +            return "PROCEDURE DEFINITION " + block.getName();
  3.1285 +         case FUNCTION_DEF:
  3.1286 +            return "FUNCTION DEFINITION " + block.getName();
  3.1287 +         case DECLARE_END:
  3.1288 +            return "DECLARE BLOCK";
  3.1289 +         case BEGIN_END:
  3.1290 +            return "BEGIN BLOCK";
  3.1291 +         case TRIGGER:
  3.1292 +            return block.getPrefix() + "TRIGGER " + block.getName();
  3.1293 +         case IF:
  3.1294 +            return block.getName();
  3.1295 +         case CASE:
  3.1296 +            return block.getName();
  3.1297 +         case WHILE_LOOP:
  3.1298 +            return "WHILE " + block.getName();
  3.1299 +         case FOR_LOOP:
  3.1300 +            return "FOR " + block.getName();
  3.1301 +         case LOOP:
  3.1302 +            return "LOOP ";
  3.1303 +         case CUSTOM_FOLD:
  3.1304 +            return block.getName();
  3.1305 +         case STATEMENT:
  3.1306 +            return block.getPrefix() + block.getName();
  3.1307 +         case CURSOR:
  3.1308 +            return "CURSOR " + block.getName();
  3.1309 +         case JAVA_SOURCE:
  3.1310 +            return block.getPrefix() + "JAVA SOURCE";
  3.1311 +         default:
  3.1312 +            return "";
  3.1313 +      }
  3.1314 +   }
  3.1315 +
  3.1316 +   /**
  3.1317 +    * Remove fold with its children
  3.1318 +    *
  3.1319 +    * @param fold
  3.1320 +    * @param blockHier
  3.1321 +    * @param fhTran
  3.1322 +    * @return true if removed all the children
  3.1323 +    */
  3.1324 +   private void removeWithChildren(final Fold fold, final List<PlsqlBlock> blockHier, final FoldHierarchyTransaction fhTran) {
  3.1325 +
  3.1326 +      final int childCount = fold.getFoldCount();
  3.1327 +      for (int i = (childCount - 1); i >= 0; i--) {
  3.1328 +         final Fold child = fold.getFold(i);
  3.1329 +         removeWithChildren(child, blockHier, fhTran);
  3.1330 +      }
  3.1331 +
  3.1332 +      //If a custom fold remove
  3.1333 +      if (fold.getType() == PlsqlFoldTypes.CUSTOM || checkExists(fold, blockHier)) {
  3.1334 +         operation.removeFromHierarchy(fold, fhTran);
  3.1335 +      }
  3.1336 +   }
  3.1337 +
  3.1338 +   /**
  3.1339 +    * Method that will check whether given fold exists in block hier
  3.1340 +    *
  3.1341 +    * @param fold
  3.1342 +    * @param blockHier
  3.1343 +    * @return
  3.1344 +    */
  3.1345 +   private boolean checkExists(final Fold fold, final List<PlsqlBlock> blockHier) {
  3.1346 +      final Comparator<PlsqlBlock> comparator = new Comparator<PlsqlBlock>() {
  3.1347 +         @Override
  3.1348 +         public int compare(final PlsqlBlock o1, final PlsqlBlock o2) {
  3.1349 +            Integer o1pos, o2pos;
  3.1350 +            if (o1.getStartOffset() > -1 && o2.getStartOffset() > -1) {
  3.1351 +               o1pos = Integer.valueOf(o1.getStartOffset());
  3.1352 +               o2pos = Integer.valueOf(o2.getStartOffset());
  3.1353 +            } else {
  3.1354 +               o1pos = Integer.valueOf(o1.getEndOffset());
  3.1355 +               o2pos = Integer.valueOf(o2.getEndOffset());
  3.1356 +            }
  3.1357 +            return o1pos.compareTo(o2pos);
  3.1358 +         }
  3.1359 +      };
  3.1360 +      return checkExists(fold, blockHier, comparator);
  3.1361 +   }
  3.1362 +
  3.1363 +   /**
  3.1364 +    * Method that will check whether given fold exists in block hier
  3.1365 +    *
  3.1366 +    * @param fold
  3.1367 +    * @param blockHier
  3.1368 +    * @param comparator
  3.1369 +    * @return
  3.1370 +    */
  3.1371 +   private boolean checkExists(final Fold fold, final List<PlsqlBlock> blockHier, final Comparator<PlsqlBlock> comparator) {
  3.1372 +      final int size = blockHier.size();
  3.1373 +      Collections.sort(blockHier, comparator);
  3.1374 +      if (size == 0) {
  3.1375 +         return false;
  3.1376 +      }
  3.1377 +      //make sure that the fold isn't before the first block or after the last block in the hierarchy.
  3.1378 +      if (fold.getStartOffset() > blockHier.get(size - 1).getEndOffset()
  3.1379 +              || fold.getEndOffset() < blockHier.get(0).getStartOffset()) {
  3.1380 +         return false;
  3.1381 +      }
  3.1382 +      for (int i = 0; i < size; i++) {
  3.1383 +         final PlsqlBlock tmp = blockHier.get(i);
  3.1384 +         if (tmp.getStartOffset() <= fold.getStartOffset() && tmp.getEndOffset() >= fold.getEndOffset()) {
  3.1385 +            return true;
  3.1386 +         }
  3.1387 +         if (tmp.getPreviousStart() <= fold.getStartOffset() && tmp.getPreviousEnd() >= fold.getEndOffset()) {
  3.1388 +            return true;
  3.1389 +         }
  3.1390 +         if ((tmp.getEndOffset() == fold.getEndOffset() || fold.getEndOffset() == tmp.getPreviousEnd()
  3.1391 +                 || tmp.getStartOffset() == fold.getStartOffset() || tmp.getPreviousStart() == fold.getStartOffset())
  3.1392 +                 && (isCorrespondingType(tmp.getType(), fold.getType()))
  3.1393 +                 && (getFoldDescription(tmp).equals(fold.getDescription()))) {
  3.1394 +            return true;
  3.1395 +         }
  3.1396 +
  3.1397 +         if (checkExists(fold, tmp.getChildBlocks(), comparator)) {
  3.1398 +            return true;
  3.1399 +         }
  3.1400 +      }
  3.1401 +
  3.1402 +      return false;
  3.1403 +   }
  3.1404 +
  3.1405 +   /**
  3.1406 +    * Method that will return the relevant block factory
  3.1407 +    *
  3.1408 +    * @return
  3.1409 +    */
  3.1410 +   private PlsqlBlockFactory getBlockFactory() {
  3.1411 +      final Object obj = doc.getProperty(Document.StreamDescriptionProperty);
  3.1412 +      if (obj instanceof Lookup.Provider) {
  3.1413 +         return ((Lookup.Provider) obj).getLookup().lookup(PlsqlBlockFactory.class);
  3.1414 +      }
  3.1415 +      return null;
  3.1416 +   }
  3.1417 +//
  3.1418 +//   /**
  3.1419 +//    * Document event has occurred
  3.1420 +//    *
  3.1421 +//    * @param o
  3.1422 +//    * @param arg
  3.1423 +//    */
  3.1424 +//   public void update(final Observable o, final Object arg) {
  3.1425 +//      final FoldHierarchyTransaction fhTran = getOperation().openTransaction();
  3.1426 +//      updateFolds(fhTran);
  3.1427 +//      // allready commited at this time
  3.1428 +//      //fhTran.commit();
  3.1429 +//   }
  3.1430 +
  3.1431 +   /**
  3.1432 +    * Private class that holds some fold info This is used to collapse folds after a change
  3.1433 +    */
  3.1434 +   private class FoldInfo {
  3.1435 +
  3.1436 +      public FoldType foldType;
  3.1437 +      public int startOffset;
  3.1438 +      public int endOffset;
  3.1439 +
  3.1440 +      private FoldInfo(final int start, final int end, final FoldType type) {
  3.1441 +         this.startOffset = start;
  3.1442 +         this.endOffset = end;
  3.1443 +         this.foldType = type;
  3.1444 +      }
  3.1445 +   }
  3.1446 +}
     4.1 --- a/PLSQL/Folding/src/org/netbeans/modules/plsql/fold/PlsqlFoldManager.java	Wed Mar 06 17:14:40 2013 +0100
     4.2 +++ b/PLSQL/Folding/src/org/netbeans/modules/plsql/fold/PlsqlFoldManager.java	Mon Mar 11 15:26:32 2013 +0100
     4.3 @@ -52,6 +52,8 @@
     4.4  import java.util.Map;
     4.5  import java.util.Observable;
     4.6  import java.util.Observer;
     4.7 +import java.util.logging.Level;
     4.8 +import java.util.logging.Logger;
     4.9  import javax.swing.event.DocumentEvent;
    4.10  import javax.swing.text.AbstractDocument;
    4.11  import javax.swing.text.BadLocationException;
    4.12 @@ -82,10 +84,12 @@
    4.13        return operation;
    4.14     }
    4.15  
    4.16 +   @Override
    4.17     public void init(final FoldOperation operation) {
    4.18        this.operation = operation;
    4.19     }
    4.20  
    4.21 +   @Override
    4.22     public void initFolds(final FoldHierarchyTransaction foldHierarchyTransaction) {
    4.23        final Document doc = getDocument();
    4.24        if (!(doc instanceof BaseDocument)) {
    4.25 @@ -123,7 +127,8 @@
    4.26     }
    4.27  
    4.28     /**
    4.29 -    * Add folds to the folder hieararchy (initialy)
    4.30 +    * Add folds to the folder hierarchy (initial)
    4.31 +    *
    4.32      * @param blockHier
    4.33      * @param fhTransaction
    4.34      */
    4.35 @@ -136,83 +141,83 @@
    4.36           final PlsqlBlockType type = temp.getType();
    4.37           String description = "";
    4.38  
    4.39 -          if (!(type == PlsqlBlockType.COMMENT && doc.getText(temp.getStartOffset(), temp.getEndOffset() - temp.getStartOffset()).indexOf("\n") == -1)) { // check for single line comments
    4.40 -              if (type == PlsqlBlockType.VIEW) {
    4.41 -                  foldType = PlsqlFoldTypes.VIEW;
    4.42 -                  description = temp.getPrefix() + "VIEW " + temp.getName();
    4.43 -              } else if (type == PlsqlBlockType.TABLE_COMMENT) {
    4.44 -                  foldType = PlsqlFoldTypes.TABLECOMMENT;
    4.45 -                  description = "COMMENT ON TABLE " + temp.getName();
    4.46 -              } else if (type == PlsqlBlockType.COLUMN_COMMENT) {
    4.47 -                  foldType = PlsqlFoldTypes.COLUMNCOMMENT;
    4.48 -                  description = "COLUMN COMMENTS ON TABLE " + temp.getName();
    4.49 -              } else if (type == PlsqlBlockType.COMMENT) {
    4.50 -                  foldType = PlsqlFoldTypes.COMMENT;
    4.51 -                  description = temp.getName();
    4.52 -              } else if (type == PlsqlBlockType.PACKAGE) {
    4.53 -                  foldType = PlsqlFoldTypes.PACKAGE;
    4.54 -                  description = temp.getPrefix() + "PACKAGE " + temp.getName();
    4.55 -              } else if (type == PlsqlBlockType.PACKAGE_BODY) {
    4.56 -                  foldType = PlsqlFoldTypes.PACKAGEBODY;
    4.57 -                  description = temp.getPrefix() + "PACKAGE BODY " + temp.getName();
    4.58 -              } else if (type == PlsqlBlockType.PROCEDURE_IMPL) {
    4.59 -                  foldType = PlsqlFoldTypes.PROCEDUREIMPL;
    4.60 -                  description = temp.getPrefix() + "PROCEDURE IMPLEMENTATION " + temp.getName();
    4.61 -              } else if (type == PlsqlBlockType.FUNCTION_IMPL) {
    4.62 -                  foldType = PlsqlFoldTypes.FUNCTIONIMPL;
    4.63 -                  description = temp.getPrefix() + "FUNCTION IMPLEMENTATION " + temp.getName();
    4.64 -              } else if (type == PlsqlBlockType.PROCEDURE_DEF) {
    4.65 -                  foldType = PlsqlFoldTypes.PROCEDUREDEF;
    4.66 -                  description = "PROCEDURE DEFINITION " + temp.getName();
    4.67 -              } else if (type == PlsqlBlockType.FUNCTION_DEF) {
    4.68 -                  foldType = PlsqlFoldTypes.FUNCTIONDEF;
    4.69 -                  description = "FUNCTION DEFINITION " + temp.getName();
    4.70 -              } else if (type == PlsqlBlockType.DECLARE_END) {
    4.71 -                  foldType = PlsqlFoldTypes.DECLAREEND;
    4.72 -                  description = "DECLARE BLOCK";
    4.73 -              } else if (type == PlsqlBlockType.BEGIN_END) {
    4.74 -                  foldType = PlsqlFoldTypes.BEGINEND;
    4.75 -                  description = "BEGIN BLOCK";
    4.76 -              } else if (type == PlsqlBlockType.TRIGGER) {
    4.77 -                  foldType = PlsqlFoldTypes.TRIGGER;
    4.78 -                  description = temp.getPrefix() + "TRIGGER " + temp.getName();
    4.79 -              } else if (type == PlsqlBlockType.IF) {
    4.80 -                  foldType = PlsqlFoldTypes.IF;
    4.81 -                  description = temp.getName();
    4.82 -              } else if (type == PlsqlBlockType.CASE) {
    4.83 -                  foldType = PlsqlFoldTypes.CASE;
    4.84 -                  description = temp.getName();
    4.85 -              } else if (type == PlsqlBlockType.WHILE_LOOP) {
    4.86 -                  foldType = PlsqlFoldTypes.WHILELOOP;
    4.87 -                  description = "WHILE " + temp.getName();
    4.88 -              } else if (type == PlsqlBlockType.FOR_LOOP) {
    4.89 -                  foldType = PlsqlFoldTypes.FORLOOP;
    4.90 -                  description = "FOR " + temp.getName();
    4.91 -              } else if (type == PlsqlBlockType.LOOP) {
    4.92 -                  foldType = PlsqlFoldTypes.LOOP;
    4.93 -                  description = "LOOP ";
    4.94 -              } else if (type == PlsqlBlockType.CUSTOM_FOLD) {
    4.95 -                  foldType = PlsqlFoldTypes.CUSTOM;
    4.96 -                  description = temp.getName();
    4.97 -              } else if (type == PlsqlBlockType.STATEMENT) {
    4.98 -                  foldType = PlsqlFoldTypes.STATEMENT;
    4.99 -                  description = temp.getPrefix() + temp.getName();
   4.100 -              } else if (type == PlsqlBlockType.CURSOR) {
   4.101 -                  foldType = PlsqlFoldTypes.CURSOR;
   4.102 -                  description = "CURSOR " + temp.getName();
   4.103 -              } else if (type == PlsqlBlockType.JAVA_SOURCE) {
   4.104 -                  foldType = PlsqlFoldTypes.JAVASOURCE;
   4.105 -                  description = temp.getPrefix() + "JAVA SOURCE";
   4.106 -              }
   4.107 +         if (!(type == PlsqlBlockType.COMMENT && doc.getText(temp.getStartOffset(), temp.getEndOffset() - temp.getStartOffset()).indexOf("\n") == -1)) { // check for single line comments
   4.108 +            if (type == PlsqlBlockType.VIEW) {
   4.109 +               foldType = PlsqlFoldTypes.VIEW;
   4.110 +               description = temp.getPrefix() + "VIEW " + temp.getName();
   4.111 +            } else if (type == PlsqlBlockType.TABLE_COMMENT) {
   4.112 +               foldType = PlsqlFoldTypes.TABLECOMMENT;
   4.113 +               description = "COMMENT ON TABLE " + temp.getName();
   4.114 +            } else if (type == PlsqlBlockType.COLUMN_COMMENT) {
   4.115 +               foldType = PlsqlFoldTypes.COLUMNCOMMENT;
   4.116 +               description = "COLUMN COMMENTS ON TABLE " + temp.getName();
   4.117 +            } else if (type == PlsqlBlockType.COMMENT) {
   4.118 +               foldType = PlsqlFoldTypes.COMMENT;
   4.119 +               description = temp.getName();
   4.120 +            } else if (type == PlsqlBlockType.PACKAGE) {
   4.121 +               foldType = PlsqlFoldTypes.PACKAGE;
   4.122 +               description = temp.getPrefix() + "PACKAGE " + temp.getName();
   4.123 +            } else if (type == PlsqlBlockType.PACKAGE_BODY) {
   4.124 +               foldType = PlsqlFoldTypes.PACKAGEBODY;
   4.125 +               description = temp.getPrefix() + "PACKAGE BODY " + temp.getName();
   4.126 +            } else if (type == PlsqlBlockType.PROCEDURE_IMPL) {
   4.127 +               foldType = PlsqlFoldTypes.PROCEDUREIMPL;
   4.128 +               description = temp.getPrefix() + "PROCEDURE IMPLEMENTATION " + temp.getName();
   4.129 +            } else if (type == PlsqlBlockType.FUNCTION_IMPL) {
   4.130 +               foldType = PlsqlFoldTypes.FUNCTIONIMPL;
   4.131 +               description = temp.getPrefix() + "FUNCTION IMPLEMENTATION " + temp.getName();
   4.132 +            } else if (type == PlsqlBlockType.PROCEDURE_DEF) {
   4.133 +               foldType = PlsqlFoldTypes.PROCEDUREDEF;
   4.134 +               description = "PROCEDURE DEFINITION " + temp.getName();
   4.135 +            } else if (type == PlsqlBlockType.FUNCTION_DEF) {
   4.136 +               foldType = PlsqlFoldTypes.FUNCTIONDEF;
   4.137 +               description = "FUNCTION DEFINITION " + temp.getName();
   4.138 +            } else if (type == PlsqlBlockType.DECLARE_END) {
   4.139 +               foldType = PlsqlFoldTypes.DECLAREEND;
   4.140 +               description = "DECLARE BLOCK";
   4.141 +            } else if (type == PlsqlBlockType.BEGIN_END) {
   4.142 +               foldType = PlsqlFoldTypes.BEGINEND;
   4.143 +               description = "BEGIN BLOCK";
   4.144 +            } else if (type == PlsqlBlockType.TRIGGER) {
   4.145 +               foldType = PlsqlFoldTypes.TRIGGER;
   4.146 +               description = temp.getPrefix() + "TRIGGER " + temp.getName();
   4.147 +            } else if (type == PlsqlBlockType.IF) {
   4.148 +               foldType = PlsqlFoldTypes.IF;
   4.149 +               description = temp.getName();
   4.150 +            } else if (type == PlsqlBlockType.CASE) {
   4.151 +               foldType = PlsqlFoldTypes.CASE;
   4.152 +               description = temp.getName();
   4.153 +            } else if (type == PlsqlBlockType.WHILE_LOOP) {
   4.154 +               foldType = PlsqlFoldTypes.WHILELOOP;
   4.155 +               description = "WHILE " + temp.getName();
   4.156 +            } else if (type == PlsqlBlockType.FOR_LOOP) {
   4.157 +               foldType = PlsqlFoldTypes.FORLOOP;
   4.158 +               description = "FOR " + temp.getName();
   4.159 +            } else if (type == PlsqlBlockType.LOOP) {
   4.160 +               foldType = PlsqlFoldTypes.LOOP;
   4.161 +               description = "LOOP ";
   4.162 +            } else if (type == PlsqlBlockType.CUSTOM_FOLD) {
   4.163 +               foldType = PlsqlFoldTypes.CUSTOM;
   4.164 +               description = temp.getName();
   4.165 +            } else if (type == PlsqlBlockType.STATEMENT) {
   4.166 +               foldType = PlsqlFoldTypes.STATEMENT;
   4.167 +               description = temp.getPrefix() + temp.getName();
   4.168 +            } else if (type == PlsqlBlockType.CURSOR) {
   4.169 +               foldType = PlsqlFoldTypes.CURSOR;
   4.170 +               description = "CURSOR " + temp.getName();
   4.171 +            } else if (type == PlsqlBlockType.JAVA_SOURCE) {
   4.172 +               foldType = PlsqlFoldTypes.JAVASOURCE;
   4.173 +               description = temp.getPrefix() + "JAVA SOURCE";
   4.174 +            }
   4.175  
   4.176 -              if (getDocument().getEndPosition().getOffset() >= temp.getEndOffset()) {
   4.177 -                  operation.addToHierarchy(foldType, description, isCollapsed(temp, foldType, collapsedFolds),
   4.178 -	      temp.getStartOffset(), temp.getEndOffset(), 0, 0, null, fhTransaction);
   4.179 +            if (getDocument().getEndPosition().getOffset() >= temp.getEndOffset()) {
   4.180 +               operation.addToHierarchy(foldType, description, isCollapsed(temp, foldType, collapsedFolds),
   4.181 +                       temp.getStartOffset(), temp.getEndOffset(), 0, 0, null, fhTransaction);
   4.182  
   4.183 -                  //check for any child folds and add them also
   4.184 -                  addFolds(temp.getChildBlocks(), fhTransaction, collapsedFolds);
   4.185 -              }
   4.186 -          }
   4.187 +               //check for any child folds and add them also
   4.188 +               addFolds(temp.getChildBlocks(), fhTransaction, collapsedFolds);
   4.189 +            }
   4.190 +         }
   4.191        }
   4.192     }
   4.193  
   4.194 @@ -226,8 +231,8 @@
   4.195     }
   4.196  
   4.197     /**
   4.198 -    * Method that will add collapsed folds (FoldInfo objects)
   4.199 -    * in the fold hierarchy to the given list
   4.200 +    * Method that will add collapsed folds (FoldInfo objects) in the fold hierarchy to the given list
   4.201 +    *
   4.202      * @param parent
   4.203      * @param foldInfoLst
   4.204      */
   4.205 @@ -244,8 +249,8 @@
   4.206     }
   4.207  
   4.208     /**
   4.209 -    * Method that will select and return the corresponding fold to parent
   4.210 -    * from oldRoot fold hierarchy
   4.211 +    * Method that will select and return the corresponding fold to parent from oldRoot fold hierarchy
   4.212 +    *
   4.213      * @param parent
   4.214      * @param foldInfoLst
   4.215      * @return
   4.216 @@ -263,8 +268,8 @@
   4.217           final FoldInfo temp = foldInfoLst.get(i);
   4.218  
   4.219           if ((temp.foldType == foldType)
   4.220 -               && (temp.startOffset == block.getPreviousStart())
   4.221 -               && (temp.endOffset == block.getPreviousEnd())) {
   4.222 +                 && (temp.startOffset == block.getPreviousStart())
   4.223 +                 && (temp.endOffset == block.getPreviousEnd())) {
   4.224              return true;
   4.225           }
   4.226        }
   4.227 @@ -274,11 +279,12 @@
   4.228  
   4.229     /**
   4.230      * Method that will update the folds based on the changes to the document
   4.231 +    *
   4.232      * @param fhTran
   4.233      */
   4.234     private synchronized void updateFolds(final FoldHierarchyTransaction fhTran) {
   4.235        try {
   4.236 -        final PlsqlBlockFactory blockFactory = getBlockFactory();
   4.237 +         final PlsqlBlockFactory blockFactory = getBlockFactory();
   4.238           if (blockFactory == null) {
   4.239              return;
   4.240           }
   4.241 @@ -331,6 +337,7 @@
   4.242  
   4.243     /**
   4.244      * Method that will change the fold descriptions if changed
   4.245 +    *
   4.246      * @param parent
   4.247      * @param blockMap
   4.248      * @param fhTran
   4.249 @@ -345,7 +352,7 @@
   4.250           if (!parent.getDescription().equals(description)) {
   4.251              operation.removeFromHierarchy(parent, fhTran);
   4.252              operation.addToHierarchy(parent.getType(), description, parent.isCollapsed(),
   4.253 -                  block.getStartOffset(), block.getEndOffset(), 0, 0, null, fhTran);
   4.254 +                    block.getStartOffset(), block.getEndOffset(), 0, 0, null, fhTran);
   4.255           }
   4.256        }
   4.257  
   4.258 @@ -354,13 +361,13 @@
   4.259           final Fold child = parent.getFold(i);
   4.260           //Ignore types that cannot have the name as an alias
   4.261           if ((child.getType() != PlsqlFoldTypes.BEGINEND)
   4.262 -               && (child.getType() != PlsqlFoldTypes.COMMENT)
   4.263 -               && (child.getType() != PlsqlFoldTypes.DECLAREEND)
   4.264 -               && (child.getType() != PlsqlFoldTypes.IF)
   4.265 -               && (child.getType() != PlsqlFoldTypes.CUSTOM)
   4.266 -               && (child.getType() != PlsqlFoldTypes.FORLOOP)
   4.267 -               && (child.getType() != PlsqlFoldTypes.LOOP)
   4.268 -               && (child.getType() != PlsqlFoldTypes.WHILELOOP)) {
   4.269 +                 && (child.getType() != PlsqlFoldTypes.COMMENT)
   4.270 +                 && (child.getType() != PlsqlFoldTypes.DECLAREEND)
   4.271 +                 && (child.getType() != PlsqlFoldTypes.IF)
   4.272 +                 && (child.getType() != PlsqlFoldTypes.CUSTOM)
   4.273 +                 && (child.getType() != PlsqlFoldTypes.FORLOOP)
   4.274 +                 && (child.getType() != PlsqlFoldTypes.LOOP)
   4.275 +                 && (child.getType() != PlsqlFoldTypes.WHILELOOP)) {
   4.276  
   4.277              changeDescriptions(child, blockMap, fhTran);
   4.278           }
   4.279 @@ -368,7 +375,8 @@
   4.280     }
   4.281  
   4.282     /**
   4.283 -    * Get the matching block to the fold. Consider the type, start &  end offset
   4.284 +    * Get the matching block to the fold. Consider the type, start & end offset
   4.285 +    *
   4.286      * @param fold
   4.287      * @param blockMap
   4.288      * @return
   4.289 @@ -385,6 +393,7 @@
   4.290  
   4.291     /**
   4.292      * Method that will check whether the block type and the fold type are matching
   4.293 +    *
   4.294      * @param blockType
   4.295      * @param foldType
   4.296      * @return
   4.297 @@ -441,8 +450,8 @@
   4.298     }
   4.299  
   4.300     /**
   4.301 -    * Method that will return the fold description given the Plsql block
   4.302 -    * Used when changing the descriptions only.
   4.303 +    * Method that will return the fold description given the Plsql block Used when changing the descriptions only.
   4.304 +    *
   4.305      * @param block
   4.306      * @return
   4.307      */
   4.308 @@ -500,44 +509,45 @@
   4.309     //FoldManager Abstract Method
   4.310     @Override
   4.311     public void insertUpdate(final DocumentEvent documentEvent, final FoldHierarchyTransaction foldHierarchyTransaction) {
   4.312 -      //We are not using this event
   4.313 +      LOG.log(Level.FINE, "insertUpdate is called");
   4.314     }
   4.315  
   4.316     //FoldManager Abstract Method
   4.317     @Override
   4.318     public void removeUpdate(final DocumentEvent documentEvent, final FoldHierarchyTransaction foldHierarchyTransaction) {
   4.319 -      //We are not using this event
   4.320 +      LOG.log(Level.FINE, "removeUpdate is called");
   4.321     }
   4.322  
   4.323     //FoldManager Abstract Method
   4.324     @Override
   4.325     public void changedUpdate(final DocumentEvent documentEvent, final FoldHierarchyTransaction foldHierarchyTransaction) {
   4.326 -      //We are not using this event
   4.327 +      LOG.log(Level.FINE, "changedUpdate is called");
   4.328     }
   4.329  
   4.330     //FoldManager Abstract Method
   4.331     @Override
   4.332     public void removeEmptyNotify(final Fold fold) {
   4.333 -      //do nothing
   4.334 +      LOG.log(Level.FINE, "removeEmptyNotify is called");
   4.335     }
   4.336  
   4.337     //FoldManager Abstract Method
   4.338     @Override
   4.339     public void removeDamagedNotify(final Fold fold) {
   4.340 -      //do nothing
   4.341 +      LOG.log(Level.FINE, "removeDamagedNotify is called");
   4.342     }
   4.343  
   4.344     //FoldManager Abstract Method
   4.345     @Override
   4.346     public void expandNotify(final Fold fold) {
   4.347 -      //do nothing
   4.348 +      LOG.log(Level.FINE, "expandNotify is called");
   4.349     }
   4.350  
   4.351     //FoldManager Abstract Method
   4.352     @Override
   4.353     public void release() {
   4.354 -      //do nothing
   4.355 +      LOG.log(Level.FINE, "release is called");
   4.356     }
   4.357 +   private static final Logger LOG = Logger.getLogger(PlsqlFoldManager.class.getName());
   4.358  
   4.359     private Document getDocument() {
   4.360        Object obj = null;
   4.361 @@ -556,6 +566,7 @@
   4.362  
   4.363     /**
   4.364      * Remove fold with its children
   4.365 +    *
   4.366      * @param fold
   4.367      * @param blockHier
   4.368      * @param fhTran
   4.369 @@ -577,13 +588,13 @@
   4.370  
   4.371     /**
   4.372      * Method that will check whether given fold exists in block hier
   4.373 +    *
   4.374      * @param fold
   4.375      * @param blockHier
   4.376      * @return
   4.377      */
   4.378     private boolean checkExists(final Fold fold, final List<PlsqlBlock> blockHier) {
   4.379        final Comparator<PlsqlBlock> comparator = new Comparator<PlsqlBlock>() {
   4.380 -
   4.381           @Override
   4.382           public int compare(final PlsqlBlock o1, final PlsqlBlock o2) {
   4.383              Integer o1pos, o2pos;
   4.384 @@ -602,6 +613,7 @@
   4.385  
   4.386     /**
   4.387      * Method that will check whether given fold exists in block hier
   4.388 +    *
   4.389      * @param fold
   4.390      * @param blockHier
   4.391      * @param comparator
   4.392 @@ -615,7 +627,7 @@
   4.393        }
   4.394        //make sure that the fold isn't before the first block or after the last block in the hierarchy.
   4.395        if (fold.getStartOffset() > blockHier.get(size - 1).getEndOffset()
   4.396 -            || fold.getEndOffset() < blockHier.get(0).getStartOffset()) {
   4.397 +              || fold.getEndOffset() < blockHier.get(0).getStartOffset()) {
   4.398           return false;
   4.399        }
   4.400        for (int i = 0; i < size; i++) {
   4.401 @@ -627,9 +639,9 @@
   4.402              return true;
   4.403           }
   4.404           if ((tmp.getEndOffset() == fold.getEndOffset() || fold.getEndOffset() == tmp.getPreviousEnd()
   4.405 -               || tmp.getStartOffset() == fold.getStartOffset() || tmp.getPreviousStart() == fold.getStartOffset())
   4.406 -               && (isCorrespondingType(tmp.getType(), fold.getType()))
   4.407 -               && (getFoldDescription(tmp).equals(fold.getDescription()))) {
   4.408 +                 || tmp.getStartOffset() == fold.getStartOffset() || tmp.getPreviousStart() == fold.getStartOffset())
   4.409 +                 && (isCorrespondingType(tmp.getType(), fold.getType()))
   4.410 +                 && (getFoldDescription(tmp).equals(fold.getDescription()))) {
   4.411              return true;
   4.412           }
   4.413  
   4.414 @@ -643,6 +655,7 @@
   4.415  
   4.416     /**
   4.417      * Method that will return the relevant block factory
   4.418 +    *
   4.419      * @return
   4.420      */
   4.421     private PlsqlBlockFactory getBlockFactory() {
   4.422 @@ -655,21 +668,21 @@
   4.423     }
   4.424  
   4.425     /**
   4.426 -    * Document event has occured
   4.427 +    * Document event has occurred
   4.428 +    *
   4.429      * @param o
   4.430      * @param arg
   4.431      */
   4.432     @Override
   4.433     public void update(final Observable o, final Object arg) {
   4.434        final FoldHierarchyTransaction fhTran = getOperation().openTransaction();
   4.435 -      updateFolds(fhTran); 
   4.436 +      updateFolds(fhTran);
   4.437        // allready commited at this time
   4.438        //fhTran.commit();
   4.439     }
   4.440  
   4.441     /**
   4.442 -    * Private class that holds some fold info
   4.443 -    * This is used to collapse folds after a change
   4.444 +    * Private class that holds some fold info This is used to collapse folds after a change
   4.445      */
   4.446     private class FoldInfo {
   4.447  
     5.1 --- a/PLSQL/Folding/src/org/netbeans/modules/plsql/fold/PlsqlFoldManagerFactory.java	Wed Mar 06 17:14:40 2013 +0100
     5.2 +++ b/PLSQL/Folding/src/org/netbeans/modules/plsql/fold/PlsqlFoldManagerFactory.java	Mon Mar 11 15:26:32 2013 +0100
     5.3 @@ -49,9 +49,15 @@
     5.4   * @author chawlk
     5.5   */
     5.6  public class PlsqlFoldManagerFactory implements FoldManagerFactory {
     5.7 -    
     5.8 -    public FoldManager createFoldManager() { 
     5.9 -        return new PlsqlFoldManager();
    5.10 -    }
    5.11 -    
    5.12 +
    5.13 +   @Override
    5.14 +   public FoldManager createFoldManager() {
    5.15 +      if (System.getProperty("plsql.fold.manager").equals("new")) {
    5.16 +         return new NewPlsqlFoldManager();
    5.17 +      }
    5.18 +      if (System.getProperty("plsql.fold.manager").equals("custom")) {
    5.19 +         return new CustomFoldManager();
    5.20 +      }
    5.21 +      return new PlsqlFoldManager();
    5.22 +   }
    5.23  }
     6.1 --- a/PLSQL/Lexer/src/org/netbeans/modules/plsql/lexer/PlsqlBlockFactory.java	Wed Mar 06 17:14:40 2013 +0100
     6.2 +++ b/PLSQL/Lexer/src/org/netbeans/modules/plsql/lexer/PlsqlBlockFactory.java	Mon Mar 11 15:26:32 2013 +0100
     6.3 @@ -472,7 +472,7 @@
     6.4      }
     6.5  
     6.6      /**
     6.7 -     * Check whether the given token offest is inlcuded in any existing block
     6.8 +     * Check whether the given token offset is included in any existing block
     6.9       * @param token
    6.10       * @param immediateBlockHier
    6.11       * @param parent
    6.12 @@ -1052,7 +1052,7 @@
    6.13      }
    6.14  
    6.15      /**
    6.16 -     * Check whether the given block is already there in block hierachy
    6.17 +     * Check whether the given block is already there in block hierarchy
    6.18       * @param block
    6.19       * @param childList
    6.20       * @return
    6.21 @@ -1291,7 +1291,7 @@
    6.22      }
    6.23  
    6.24      /**
    6.25 -     * trigged when opening a different document
    6.26 +     * triggered when opening a different document
    6.27       * @param e
    6.28       */
    6.29      @Override