blob: 54d9a0f3b6a7d1ce5809d89eaa3a2f6eaaf8ae81 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2014, 2021 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.ecommons.text.core.treepartitioner;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.eclipse.jface.text.Position;
import org.eclipse.statet.jcommons.collections.ImCollections;
import org.eclipse.statet.jcommons.collections.ImList;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.lang.Nullable;
@NonNullByDefault
abstract class NodePosition extends Position implements TreePartitionNode {
static final int indexOf(final List<NodePosition> children, final int offset) {
int begin= 0;
int end= children.size() - 1;
while (begin <= end) {
int i= (begin + end) >>> 1;
NodePosition child= children.get(i);
if (child.offset > offset) {
end= i - 1;
}
// !(child.offset == offset || child.offset + child.length > offset)
else if (child.offset != offset && child.offset + child.length <= offset) {
begin= i + 1;
}
else {
for (; i > 0; i--) {
child= children.get(i - 1);
if (child.offset != offset && child.offset + child.length <= offset) {
break;
}
}
return i;
}
}
return -(begin + 1);
}
static final class CommonNode extends NodePosition {
public CommonNode(final NodePosition parent, final int offset, final int length,
final TreePartitionNodeType type, final int stamp) {
super(parent, offset, length, type, stamp);
}
@Override
public int getStartOffset() {
return this.offset;
}
@Override
public int getEndOffset() {
return this.offset + this.length;
}
@Override
public int getLength() {
return this.length;
}
}
private static final List<NodePosition> NO_CHILDREN= Collections.emptyList();
private static final ImList<Object> NO_ATTACHMENTS= ImCollections.emptyList();
@Nullable NodePosition parent;
/**
* Sorted list of children
*/
List<NodePosition> children= NodePosition.NO_CHILDREN;
TreePartitionNodeType type;
int stamp;
int flags;
private volatile ImList<Object> attachments= NO_ATTACHMENTS;
public NodePosition(final @Nullable NodePosition parent, final int offset, final int length,
final TreePartitionNodeType type, final int stamp) {
super(offset, length);
this.parent= parent;
this.type= type;
this.stamp= stamp;
}
@Override
public abstract int getStartOffset();
@Override
public abstract int getEndOffset();
@Override
public abstract int getLength();
@Override
public int getFlags() {
return this.flags;
}
final void insertChild(final int childIdx, final NodePosition child) {
assert (child.parent == this);
if (this.children == NO_CHILDREN) {
this.children= new ArrayList<>(8);
}
this.children.add(childIdx, child);
}
private final void removeChild(final NodePosition child) {
assert (child.parent == this);
child.parent= null;
if (this.isDeleted) {
return;
}
// note: if child is deleted, the position may be out-of-sync
for (int idx= 0; idx < this.children.size(); idx++) {
if (this.children.get(idx) == child) {
this.children.remove(idx);
return;
}
}
assert (false);
}
@Override
public final void delete() {
super.delete();
final NodePosition parent= this.parent;
if (parent != null) {
parent.removeChild(this);
}
}
@Override
public final void undelete() {
throw new UnsupportedOperationException();
}
@Override
public final TreePartitionNodeType getType() {
return this.type;
}
@Override
public @Nullable TreePartitionNode getParent() {
return this.parent;
}
@Override
public final int getChildCount() {
return this.children.size();
}
@Override
public final TreePartitionNode getChild(final int idx) {
return this.children.get(idx);
}
@Override
public final int indexOfChild(final int offset) {
return indexOf(this.children, offset);
}
@Override
public final int indexOfChild(final TreePartitionNode child) {
int idx= indexOf(this.children, child.getStartOffset());
assert (idx >= 0);
do {
if (this.children.get(idx) == child) {
return idx;
}
}
while (++idx < this.children.size());
// for (idx= 0; idx < this.children.size(); idx++) {
// if (this.children.get(idx) == child) {
// return idx;
// }
// }
throw new IllegalArgumentException("child"); //$NON-NLS-1$
}
@Override
public synchronized void addAttachment(final Object data) {
this.attachments= ImCollections.addElement(this.attachments, data);
}
@Override
public synchronized void removeAttachment(final Object data) {
this.attachments= ImCollections.removeElement(this.attachments, data);
}
@Override
public ImList<Object> getAttachments() {
return this.attachments;
}
@Override
public String toString() {
final StringBuilder sb= new StringBuilder("NodePosition ["); //$NON-NLS-1$
sb.append(getOffset());
sb.append(", "); //$NON-NLS-1$
sb.append(getOffset() + getLength());
sb.append(") "); //$NON-NLS-1$
if (isDeleted()) {
sb.append("<deleted> "); //$NON-NLS-1$
}
sb.append(this.type);
return sb.toString();
}
}