Overview

# NetBeans Architecture Answers for editor/fold module

- Author: mmetelka@netbeans.org
- Answers as of: Dec 7, 2006
- Answers for questions version: 1.24
- Latest available version of questions: 1.26

WARNING: answering questions version 1.24 rather than the current 1.26.

## Interfaces table

##### Group of java interfaces

Interface Name | In/Out | Stability | Specified in What Document?
CodeFoldingAPI | Exported | Official |
FilesystemsAPI | Imported | Official | .../openide/filesystems/doc-files/api.html
LoadersAPI | Imported | Official | .../org/openide/loaders/doc-files/api.html

## General Information

Question (arch-what): What is this project good for?

Answer: The Code Folding is part of the editor module functionality and it's responsible for hiding of the portions of the code that are less important for the user at the given time.

Question (arch-overall): Describe the overall architecture.

Answer: The Code Folding was created to address requirements described in Code Folding UI Specification

It is a module located under /cvs/editor/fold directory.

It consists of

- API in org.netbeans.api.editor.fold
- SPI in org.netbeans.spi.editor.fold
- Implementation in org.netbeans.modules.editor.fold

Code Folding defines CodeFoldingAPI.

The Javadoc documentation can be generated by using

        cd /cvs/editor/fold
        ant javadoc

Question (arch-usecases): Describe the main use cases of the new API. Who will use it under what circumstances? What kind of code would typically need to be written to use the module?

Answer:

## API Use Cases

### Exploring of the Folds

The code folding structure (fold hierarchy) relates to `javax.swing.JTextComponent` instance in one-to-one relationship.
To find the code folding hierarchy instance for the given non-null text component the following code snippet can be used:

        JTextComponent editorComponent = ...
        FoldHierarchy hierarchy = FoldHierarchy.get(editorComponent);

### Explore the Folds Hierarchy

The tree-based hierarchy has one non-removable and non-collapsable root fold that covers the whole document. It can be obtained by

        FoldHierarchy foldHierarchy = ...
        Fold rootFold = hierarchy.getRootFold();

The children folds of the root fold (or children folds) can be obtained by

        // the hierarchy must be locked prior exploration or manipulation
        hierarchy.lock();
        try {
            Fold rootFold = ...
            int foldCount = rootFold.getFoldCount();
            for (int i = 0; i < foldCount; i++) {
                Fold childFold = rootFold.getFold(i);
            }
        } finally {
            hierarchy.unlock();
        }

Index of the child in its parent can be found by

        hierarchy.lock();
        try {
            Fold rootFold = ...
            int foldIndex = rootFold.getFoldIndex(childFold);
        } finally {
            hierarchy.unlock();
        }

### Collapse Nearest Fold

In the given fold hierarchy find the nearest fold right at or after the given offset and collapse it.

        hierarchy.lock();
        try {
            Fold fold = FoldUtilities.findNearestFold(hierarchy, offset);
            hierarchy.collapse(fold);
        } finally {
            hierarchy.unlock();
        }

### Expand All Folds

In the given fold hierarchy expand all folds that are currently collapsed.

        FoldUtilities.expand(hierarchy, null);

### Collapse All Folds of Certain Type

In the given fold hierarchy collapse all e.g. javadoc folds that are currently collapsed.
The example can be generalized to any fold type.

        FoldUtilities.collapse(hierarchy, JAVADOC_FOLD_TYPE);

### Force Fold Expansion for Caret Moving Into Collapsed Fold

In the given fold hierarchy expand the fold into which the caret is going to be moved by `Caret.setDot(offset)`.
The hierarchy must be locked and this example assumes that the underlying document is already read-locked e.g. by `Document.render()`.

        FoldHierarchy hierarchy = FoldHierarchy.get(caretComponent);
        hierarchy.lock();
        try {
            Fold collapsed = FoldUtilities.findCollapsedFold(hierarchy, offset, offset);
            if (collapsed != null && collapsed.getStartOffset() < offset &&
                collapsed.getEndOffset() > offset) {
                hierarchy.expand(collapsed);
            }
        } finally {
            hierarchy.unlock();
        }

### Start Listening on Fold Hierarchy Changes

In the given fold hierarchy start to listen on all changes done in the hierarchy.
This is actually used e.g. in the Editor's View Hierarchy that needs to refresh views based on the fold changes.

        hierarchy.addFoldHierarchyListener(new FoldHierarchyListener() {
            public void foldHierarchyChanged(FoldHierarchyEvent evt) {
                // Hierarchy does not need to be locked here
                //
                // evt.getAffectedStartOffset() and getAffectedEndOffset()
                // give text area affected by the fold changes in the event
            }
        });

### Inspect Collapsed Folds in Affected Area

Listen on the hierarchy changes and refresh the views in the text area affected by the fold change.
Inspect the collapsed folds in the affected area because special views need to be created for the collapsed folds.
The actual code in the View Hierarchy is somewhat different but the one given here is more descriptive.

        hierarchy.addFoldHierarchyListener(new FoldHierarchyListener() {
            public void foldHierarchyChanged(FoldHierarchyEvent evt) {
                for (Iterator collapsedFoldIterator
                    = FoldUtilities.collapsedFoldIterator(hierarchy,
                        evt.getAffectedStartOffset(),
                        evt.getAffectedEndOffset()
                    );
                    it.hasNext();
                ) {
                    Fold collapsedFold = (Fold)it.next();
                    // Create special view for the collapsedFold
                }
            }
        });

## SPI Use Cases

### Create a New Fold Manager

Manipulation of the folds is designed to be done by fold managers.
Those classes implement `FoldManager` interface in the SPI.
At initialization time they are given instance of `FoldOperation` through which they can create, add or remove the fold instances.

To create and use a new `FoldManager` instance it's necessary to

- Define the class of the FoldManager.

          public class MyFoldManager implements FoldManager { // or extends AbstractFoldManager

              ...

          }

- Create FoldManagerFactory for the FoldManager.

          public class MyFoldManager ...

              ...

              public static final class Factory implements FoldManagerFactory {

                  public FoldManager createFoldManager() {
                      return new MyFoldManager();
                  }

              }

          }

- Register FoldManagerFactory into xml layer into the directory "Editors/<mime-type>/FoldManager/"
- Enable Code Folding in editor's Settings initializer (please see e.g. `NbJavaSettingsInitializer`)

          public class MySettingsInitializer ...

              public void updateSettingsMap(Class kitClass, Map settingsMap) {
                  ...
                  settingsMap.put(SettingsNames.CODE_FOLDING_ENABLE, Boolean.TRUE);
              }

          }

### Create a New Fold by Fold Manager

Create a new fold and add it to the hierarchy. The operation is performed by the fold manager either at initialization phase (in the `initFolds()` which gets called automatically by the infrastructure) or at any other time when the fold manager's operation gets invoked (usually by a listener that the fold manager attaches to be notified about changes that can cause the folds structure to be changed - e.g. a parsing listener for java folds).

Operations that manipulate the hierarchy are done in terms of a valid transaction over the fold hierarchy.
Transactions allow to fire the collected changes as a single `FoldHierarchyEvent` at the time when they are committed.

        // In the FoldManager's context
        FoldOperation operation = getOperation();
        FoldHierarchyTransaction transaction = operation.openTransaction();
        try {
            Fold fold = operation.createFold(...);
            operation.addFoldToHierarchy(fold, transaction);
        } finally {
            transaction.commit();
        }

### Remove Fold from Hierarchy by Fold Manager

Remove the existing fold from the hierarchy

        // In the FoldManager's context
        FoldOperation operation = getOperation();
        FoldHierarchyTransaction transaction = operation.openTransaction();
        try {
            Fold fold = ...
            operation.removeFoldFromHierarchy(fold, transaction);
        } finally {
            transaction.commit();
        }

Question (arch-time): What are the time estimates of the work?

Answer: The cvs/editor/fold module is currently maintained under fold_api branch of the editor module.

Question (arch-quality): How will the quality of your code be tested and how are future regressions going to be prevented?

Answer: The unit tests will be created to cover the functionality testing.

We are also considering to create randomized tests that would construct artificial fold managers and randomly insert/remove folds and do random modifications to the file. This way we could make a stress test for the fold merging algorithms present in FoldHierarchyTransaction (and FoldHierarchySpi).

Question (arch-where): Where one can find sources for your module?

WARNING: Question with id="arch-where" has not been answered!

## Project and platform dependencies

Question (dep-nb): What other NetBeans projects and modules does this one depend on?

Answer: Code Folding has dependency on FileSystems FilesystemsAPI. and Loaders LoadersAPI.

Specific fold managers can have their own additional dependencies (e.g. java fold manager depends on Java Source Hierarchy etc.).

Question (dep-non-nb): What other projects outside NetBeans does this one depend on?

Answer: No other projects.

Question (dep-platform): On which platforms does your module run? Does it run in the same way on each?

Answer: All platforms.

Question (dep-jre): Which version of JRE do you need (1.2, 1.3, 1.4, etc.)?

Answer: JDK1.4 and higher can be used.

Question (dep-jrejdk): Do you require the JDK or is the JRE enough?

Answer: JRE is sufficient.

## Deployment

Question (deploy-jar): Do you deploy just module JAR file(s) or other files as well?

Answer: No additional files.

Question (deploy-nbm): Can you deploy an NBM via the Update Center?

Answer: Yes.

Question (deploy-shared): Do you need to be installed in the shared location only, or in the user directory only, or can your module be installed anywhere?

Answer: Anywhere.

Question (deploy-packages): Are packages of your module made inaccessible by not declaring them public?

Answer: Yes, only the API and SPI are public. The implementation is not public.

Question (deploy-dependencies): What do other modules need to do to declare a dependency on this one?

Answer:

    OpenIDE-Module-Module-Dependencies: org.netbeans.modules.editor.fold/1 > 1.5.22

## Compatibility with environment

Question (compat-i18n): Is your module correctly internationalized?

Answer: Yes.

Question (compat-standards): Does the module implement or define any standards? Is the implementation exact or does it deviate somehow?

Answer: Compaible with standards.

Question (compat-version): Can your module coexist with earlier and future versions of itself? Can you correctly read all old settings? Will future versions be able to read your current settings? Can you read or politely ignore settings stored by a future version?

Answer: The CF presets are stored as part of editor settings.
We plan to introduce fold state persistence sometimes in the future so the collapsed fold status would persist for individual files.

## Access to resources

Question (resources-file): Does your module use `java.io.File` directly?

Answer: No.

Question (resources-layer): Does your module provide own layer? Does it create any files or folders in it? What it is trying to communicate by that and with which components?

Answer: No.

Question (resources-read): Does your module read any resources from layers? For what purpose?

Answer: Module reads fold managers registered fold manager factories for the given mime-type from the Editors/<mime-type>/FoldManager folder.

Question (resources-mask): Does your module mask/hide/override any resources provided by other modules in their layers?

Answer: No.

## Lookup of components

Question (lookup-lookup): Does your module use `org.openide.util.Lookup` or any similar technology to find any components to communicate with? Which ones?

Answer: No

Question (lookup-register): Do you register anything into lookup for other code to find?

Answer: No.

Question (lookup-remove): Do you remove entries of other modules from lookup?

Answer: No.

## Execution Environment

Question (exec-property): Is execution of your code influenced by any environment or Java system (`System.getProperty`) property?

Answer: No.

Question (exec-component): Is execution of your code influenced by any (string) property of any of your components?

Answer: The FoldHierarchy instance is physically stored as client property of the text component with key FoldHierarchy.class.

Question (exec-ant-tasks): Do you define or register any ant tasks that other can use?

WARNING: Question with id="exec-ant-tasks" has not been answered!

Question (exec-classloader): Does your code create its own class loader(s)?

Answer: No.

Question (exec-reflection): Does your code use Java Reflection to execute other code?

Answer: No.

Question (exec-privateaccess): Are you aware of any other parts of the system calling some of your methods by reflection?

Answer: No.

Question (exec-process): Do you execute an external process from your module? How do you ensure that the result is the same on different platforms? Do you parse output? Do you depend on result code?

Answer: No.

Question (exec-introspection): Does your module use any kind of runtime type information (`instanceof`, work with `java.lang.Class`, etc.)?

Answer: No.

Question (exec-threading): What threading models, if any, does your module adhere to?

Answer: FoldHierarchy needs to be locked exclusively prior manipulation; `FoldHierarchy.render(Runnable)` (or `FoldHierarchy.lock()`) can be used to lock the hierarchy for an exclusive use.
Locking of the hierarchy should be preceded by document locking that ensures document model stability.

Question (security-policy): Does your functionality require modifications to the standard policy file?

Answer: No.

Question (security-grant): Does your code grant additional rights to some other code?

Answer: No.

## Format of files and protocols

Question (format-types): Which protocols and file formats (if any) does your module read or write on disk, or transmit or receive over the network? Do you generate an ant build script? Can it be edited and modified?

Answer: No files read or written to the disk.

Question (format-dnd): Which protocols (if any) does your code understand during Drag & Drop?

Answer: No D&D.

Question (format-clipboard): Which data flavors (if any) does your code read from or insert to the clipboard (by access to clipboard on means calling methods on `java.awt.datatransfer.Transferable`?

Answer: No clipboard support.

## Performance and Scalability

Question (perf-startup): Does your module run any code on startup?

Answer: No.

Question (perf-exit): Does your module run any code on exit?

Answer: No.

Question (perf-scale): Which external criteria influence the performance of your program (size of file in editor, number of files in menu, in source directory, etc.) and how well your code scales?

Answer: Number of methods/classes/javadocs in a java source file. Those things typically scales with size of the file.

Question (perf-limit): Are there any hard-coded or practical limits in the number or size of elements your code can handle?

Answer: No limits when assuming Integer.MAX_VALUE (for number of folds etc.) is not a practical limit.

Question (perf-mem): How much memory does your component consume? Estimate with a relation to the number of windows, etc.

Answer: A fold instance (e.g. DescriptionFold) should take about 192 bytes. There are typically tens to hundreds of folds in a typical java file.

Question (perf-wakeup): Does any piece of your code wake up periodically and do something even when the system is otherwise idle (no user interaction)?

Answer: No.

Question (perf-progress): Does your module execute any long-running tasks?

Answer: The creation and updating of the folds for large files are potentially long-running tasks.
The editor infrastructure can consult the fold hierarchy for the fold presence (for the particular text component) at any time (not strictly at the file opening time).
In general if there are no folding presets (certain fold types collapsed at the file opening time) the folds creation can be delayed after the document is displayed initially.

Typically the physical fold creation and updating is fast compared to the collecting of the underlying information. For example collecting of the information for java folds requires consulting of parser information which may not be present at the time yet so this may result in even a few seconds delay waiting for document parsing.
Therefore the fold managers are encouraged to prepare the fold-related data (resulting into fold boundary positions) in a separate thread and schedule the physical fold creation and fold hierarchy updates (which are reasonably fast) into EDT thread (due to consistency purposes as the EDT controls the document switching in the editor text component).
The FoldManager class javadoc contains the guiding information as well.

Question (perf-huge_dialogs): Does your module contain any dialogs or wizards with a large number of GUI controls such as combo boxes, lists, trees, or text areas?

Answer: No.

Question (perf-menus): Does your module use dynamically updated context menus, or context-sensitive actions with complicated and slow enablement logic?

Answer: No.

Question (perf-spi): How the performance of the plugged in code will be enforced?

Answer: Binary searches are used for all the operations. There should be no linear scans in critical parts of the Code Folding infrastructure.

Built on December 7 2006. | Portions Copyright 1997-2005 Sun Microsystems, Inc. All rights reserved.