diff --git a/openide.io/apichanges.xml b/openide.io/apichanges.xml --- a/openide.io/apichanges.xml +++ b/openide.io/apichanges.xml @@ -107,6 +107,23 @@ + + + Created new user-friendly methods for working with FoldHandle instances. + + + + + +

+ Added methods that provide better info about state of fold + handles, and methods that handle unexpected situations silently + instead of throwing exceptions. +

+
+ + +
Added LOG_SUCCESS, LOG_FAILURE, LOG_WARNING and LOG_DEBUG into IOColors.OutputType to be able to specify a color of logging messages. diff --git a/openide.io/manifest.mf b/openide.io/manifest.mf --- a/openide.io/manifest.mf +++ b/openide.io/manifest.mf @@ -1,6 +1,6 @@ Manifest-Version: 1.0 OpenIDE-Module: org.openide.io -OpenIDE-Module-Specification-Version: 1.41 +OpenIDE-Module-Specification-Version: 1.42 OpenIDE-Module-Localizing-Bundle: org/openide/io/Bundle.properties OpenIDE-Module-Recommends: org.openide.windows.IOProvider, org.openide.windows.IOContainer$Provider AutoUpdate-Essential-Module: true diff --git a/openide.io/src/org/openide/windows/FoldHandle.java b/openide.io/src/org/openide/windows/FoldHandle.java --- a/openide.io/src/org/openide/windows/FoldHandle.java +++ b/openide.io/src/org/openide/windows/FoldHandle.java @@ -42,6 +42,9 @@ package org.openide.windows; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.netbeans.api.annotations.common.CheckForNull; import org.openide.windows.IOFolding.FoldHandleDefinition; /** @@ -54,6 +57,9 @@ public final class FoldHandle { private final FoldHandleDefinition definition; + private static final Logger LOG = Logger.getLogger(FoldHandle.class.getName()); + private FoldHandle currentChild; + private boolean finished = false; FoldHandle(FoldHandleDefinition definition) { this.definition = definition; @@ -67,6 +73,7 @@ */ public void finish() { definition.finish(); + finished = true; } /** @@ -75,10 +82,12 @@ * @param expanded True to expand the new fold, false to collapse it, parent * folds will not be collapsed/expanded. * @return Handle for the newly created fold. - * @throws IllegalStateException if the fold has been already finished. + * @throws IllegalStateException if the fold has been already finished, or + * if an unfinished nested fold exists. */ public FoldHandle startFold(boolean expanded) { - return new FoldHandle(definition.startFold(expanded)); + currentChild = new FoldHandle(definition.startFold(expanded)); + return currentChild; } /** @@ -91,4 +100,93 @@ public void setExpanded(boolean expanded) { definition.setExpanded(expanded); } + + /** + * Check whether this fold handle has been finished. + * + * @return True if {@link #finish()} or {@link #silentFinish()} has been + * already called on this fold handle, false otherwise. + * + * @since openide.io/1.42 + */ + public boolean isFinished() { + return finished; + } + + /** + * Get handle created by the last invocation of {@link #startFold(boolean)} + * or {@link #silentStartFold(boolean)}. The handle can be finished or + * unfinished. + * + * @return The last started nested fold. Can be null. + * + * @since openide.io/1.42 + */ + public @CheckForNull FoldHandle getLastNestedFold() { + return currentChild; + } + + /** + * Get current nested fold. Similar to {@link #getLastNestedFold()}, but + * returns null if the last nested fold has been already finished. + * + * @return The last unfinished nested fold or null. + * + * @since openide.io/1.42 + */ + public @CheckForNull FoldHandle getCurrentNestedFold() { + return (currentChild != null && !currentChild.isFinished()) + ? currentChild + : null; + } + + /** + * Similar to {@link #finish()}, but no exception is thrown if the fold + * handle has been already finished. If an unfinished child fold exists, it + * will be finished too. Any exception that could happen will be caught and + * logged. + * + * @since openide.io/1.42 + */ + public void silentFinish() { + if (!finished) { + if (currentChild != null && !currentChild.finished) { + currentChild.silentFinish(); + } + try { + finish(); + } catch (Exception e) { + LOG.log(Level.FINE, null, e); + } + } + } + + /** + * Similar to {@link #startFold(boolean)}, but no exception is thrown if the + * fold is already finished, as well as if an unfinished nested fold exists. + * If an unfinished nested fold exists, it will be finished before creation + * of the new one. + * + * @param expanded True to expand the new fold, false to collapse it, parent + * folds will not be collapsed/expanded. + * @return The new fold handle, or null if it cannot be created. + * + * @since openide.io/1.42 + */ + public @CheckForNull FoldHandle silentStartFold(boolean expanded) { + if (!finished) { + if (currentChild != null && !currentChild.finished) { + currentChild.silentFinish(); + } + try { + return startFold(expanded); + } catch (Exception e) { + LOG.log(Level.FINE, null, e); + return null; + } + } else { + LOG.log(Level.FINE, "silentStartFold - already finished"); //NOI18N + return null; + } + } } diff --git a/openide.io/test/unit/src/org/openide/windows/IOFoldingTest.java b/openide.io/test/unit/src/org/openide/windows/IOFoldingTest.java --- a/openide.io/test/unit/src/org/openide/windows/IOFoldingTest.java +++ b/openide.io/test/unit/src/org/openide/windows/IOFoldingTest.java @@ -96,6 +96,44 @@ assertFalse(IOFolding.isSupported(new DummyInputOutput())); } + @Test + public void testIsFinished() { + InputOutputWithFolding io = new InputOutputWithFolding(); + FoldHandle fold1 = IOFolding.startFold(io, true); + assertFalse(fold1.isFinished()); + FoldHandle fold2 = fold1.startFold(true); + assertFalse(fold2.isFinished()); + fold2.finish(); + assertTrue(fold2.isFinished()); + fold1.finish(); + assertTrue(fold1.isFinished()); + } + + @Test + public void testSilentFinish() { + InputOutputWithFolding io = new InputOutputWithFolding(); + FoldHandle fold1 = IOFolding.startFold(io, true); + FoldHandle fold2 = fold1.startFold(true); + fold1.silentFinish(); + assertTrue(fold2.isFinished()); + fold1.silentFinish(); // no exception when calling again + fold1.silentFinish(); // still no exception + } + + @Test + public void testSilentFoldStart() { + InputOutputWithFolding io = new InputOutputWithFolding(); + FoldHandle fold1 = IOFolding.startFold(io, true); + FoldHandle fold2 = fold1.startFold(true); + FoldHandle fold3 = fold1.silentStartFold(true); + assertTrue(fold2.isFinished()); + assertNotNull(fold3); + fold1.silentFinish(); + assertTrue(fold1.isFinished()); + FoldHandle fold4 = fold1.silentStartFold(true); + assertNull(fold4); + } + /** * Dummy InputOutput that supports folding. */ @@ -129,6 +167,7 @@ private DummyFoldHandleDef nested = null; private final DummyFoldHandleDef parent; + private boolean finished = false; public DummyFoldHandleDef(DummyFoldHandleDef parent) { this.parent = parent; @@ -144,11 +183,14 @@ parent.nested = null; } currentLevel--; + finished = true; } @Override public FoldHandleDefinition startFold(boolean expanded) { - if (nested != null) { + if (finished) { + throw new IllegalStateException("Already finished."); + } else if (nested != null) { throw new IllegalStateException("Last fold not finished."); } else { nested = new DummyFoldHandleDef(this);