| /*=============================================================================# |
| # 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(); |
| } |
| |
| } |