From 386fb544def738b3fa0b9f2246a7ad70095d2967 Mon Sep 17 00:00:00 2001 From: Elizabeth Date: Wed, 22 May 2024 11:28:36 +0300 Subject: [PATCH] dbeaver/pro#2860 Fix NPE on sql text editing --- .../model/sql/semantics/OffsetKeyedTreeMap.java | 8 +++++++- .../model/sql/semantics/SQLDocumentSyntaxContext.java | 10 +++++----- .../dbeaver/model/lsm/test/OffsetKeyedTreeMapTest.java | 2 ++ 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/semantics/OffsetKeyedTreeMap.java b/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/semantics/OffsetKeyedTreeMap.java index 2d1c8df93ab1..6d45f11dc3d6 100644 --- a/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/semantics/OffsetKeyedTreeMap.java +++ b/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/semantics/OffsetKeyedTreeMap.java @@ -16,6 +16,8 @@ */ package org.jkiss.dbeaver.model.sql.semantics; +import org.jkiss.code.Nullable; + import java.util.Iterator; import java.util.Set; import java.util.function.BiConsumer; @@ -410,6 +412,7 @@ public NodesIterator nodesIteratorAt(int position) { NodeAndParentAtOffset initialLocation = this.findImpl(position); return switch (this.size) { case 0 -> new NodesIterator() { + @Nullable @Override public T getCurrValue() { return null; @@ -433,7 +436,9 @@ public boolean next() { case 1 -> new NodesIterator() { boolean beforeFirst = position < OffsetKeyedTreeMap.this.root.offset; boolean afterLast = position > OffsetKeyedTreeMap.this.root.offset; - Node theOnlyNode = OffsetKeyedTreeMap.this.root; + final Node theOnlyNode = OffsetKeyedTreeMap.this.root; + + @Nullable @Override public T getCurrValue() { return !this.beforeFirst && !this.afterLast ? this.theOnlyNode.content : null; @@ -479,6 +484,7 @@ public boolean next() { initialLocation.node.isSentinel() ? position : position - initialLocation.node.offset ); + @Nullable @Override public T getCurrValue() { return this.currentLocation.node == null ? null : this.currentLocation.node.content; diff --git a/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/semantics/SQLDocumentSyntaxContext.java b/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/semantics/SQLDocumentSyntaxContext.java index 30d08d213b41..0f65361a1944 100644 --- a/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/semantics/SQLDocumentSyntaxContext.java +++ b/plugins/org.jkiss.dbeaver.model.sql/src/org/jkiss/dbeaver/model/sql/semantics/SQLDocumentSyntaxContext.java @@ -207,7 +207,7 @@ public IRegion applyDelta(int offset, int oldLength, int newLength) { keyOffsetsToRemove = ListNode.push(keyOffsetsToRemove, currOffset); this.forEachListener(l -> l.onScriptItemInvalidated(currItem)); lastAffectedOffset = currOffset + currItem.length(); - } else if (it.prev()) { + } else if (it.prev() && it.getCurrValue() != null) { currOffset = it.getCurrOffset(); SQLDocumentScriptItemSyntaxContext currItem2 = it.getCurrValue(); if (currOffset <= offset && currOffset + currItem2.length() > offset) { @@ -218,7 +218,7 @@ public IRegion applyDelta(int offset, int oldLength, int newLength) { } else { lastAffectedOffset = offset + oldLength; } - while (it.next() && (delta < 0 || lastAffectedOffset <= (offset + oldLength))) { + while (it.next() && it.getCurrValue() != null && (delta < 0 || lastAffectedOffset <= (offset + oldLength))) { currOffset = it.getCurrOffset(); SQLDocumentScriptItemSyntaxContext currItem3 = it.getCurrValue(); keyOffsetsToRemove = ListNode.push(keyOffsetsToRemove, currOffset); @@ -249,7 +249,7 @@ public IRegion applyDelta(int offset, int oldLength, int newLength) { affectedRegion = new Region(scriptItem.offset, scriptItem.item.length()); } else { NodesIterator it = this.scriptItems.nodesIteratorAt(offset); - int start = it.prev() ? it.getCurrOffset() + it.getCurrValue().length() : 0; + int start = it.prev() && it.getCurrValue() != null ? it.getCurrOffset() + it.getCurrValue().length() : 0; int length = it.next() ? (it.getCurrOffset() - start) : Integer.MAX_VALUE; affectedRegion = new Region(start, length); } @@ -289,7 +289,7 @@ public Interval dropInvisibleScriptItems(@NotNull Interval actualFragment) { keyOffsetsToRemove = ListNode.push(keyOffsetsToRemove, off1); this.forEachListener(l -> l.onScriptItemInvalidated(it1.getCurrValue())); } - while (it1.next() && (off1 = it1.getCurrOffset()) + it1.getCurrValue().length() < rangeStart) { + while (it1.next() && it1.getCurrValue() != null && (off1 = it1.getCurrOffset()) + it1.getCurrValue().length() < rangeStart) { keyOffsetsToRemove = ListNode.push(keyOffsetsToRemove, off1); this.forEachListener(l -> l.onScriptItemInvalidated(it1.getCurrValue())); } @@ -301,7 +301,7 @@ public Interval dropInvisibleScriptItems(@NotNull Interval actualFragment) { keyOffsetsToRemove = ListNode.push(keyOffsetsToRemove, off2); this.forEachListener(l -> l.onScriptItemInvalidated(it2.getCurrValue())); } - while (it2.prev() && (off2 = it2.getCurrOffset()) > rangeEnd) { + while (it2.prev() && it2.getCurrValue() != null && (off2 = it2.getCurrOffset()) > rangeEnd) { keyOffsetsToRemove = ListNode.push(keyOffsetsToRemove, off2); this.forEachListener(l -> l.onScriptItemInvalidated(it2.getCurrValue())); } diff --git a/test/org.jkiss.dbeaver.model.lsm.test/src/org/jkiss/dbeaver/model/lsm/test/OffsetKeyedTreeMapTest.java b/test/org.jkiss.dbeaver.model.lsm.test/src/org/jkiss/dbeaver/model/lsm/test/OffsetKeyedTreeMapTest.java index 0251ce1b27db..cbaa212acc09 100644 --- a/test/org.jkiss.dbeaver.model.lsm.test/src/org/jkiss/dbeaver/model/lsm/test/OffsetKeyedTreeMapTest.java +++ b/test/org.jkiss.dbeaver.model.lsm.test/src/org/jkiss/dbeaver/model/lsm/test/OffsetKeyedTreeMapTest.java @@ -17,6 +17,7 @@ package org.jkiss.dbeaver.model.lsm.test; import org.antlr.v4.runtime.misc.Interval; +import org.jkiss.code.Nullable; import org.jkiss.dbeaver.model.sql.semantics.OffsetKeyedTreeMap; import org.jkiss.dbeaver.model.stm.STMUtils; import org.junit.Assert; @@ -267,6 +268,7 @@ public int getCurrOffset() { } } + @Nullable @Override public T getCurrValue() { return this.index >= 0 && this.index < list.size() ? list.get(this.index).data : null;