| /*=============================================================================# |
| # 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.io.IOException; |
| import java.io.Writer; |
| import java.util.List; |
| |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.TextUtilities; |
| |
| import org.eclipse.statet.jcommons.lang.NonNullByDefault; |
| import org.eclipse.statet.jcommons.lang.Nullable; |
| import org.eclipse.statet.jcommons.text.core.BasicTextRegion; |
| import org.eclipse.statet.jcommons.text.core.TextRegion; |
| |
| import org.eclipse.statet.ecommons.text.core.PartitionConstraint; |
| |
| |
| @NonNullByDefault |
| public class TreePartitionUtils { |
| |
| |
| public static class PartitionPrinter { |
| |
| |
| private final Writer writer; |
| |
| private @Nullable IDocument document; |
| |
| private int maxFragmentSize= 25; |
| |
| |
| public PartitionPrinter(final Writer writer) { |
| if (writer == null) { |
| throw new NullPointerException("writer"); //$NON-NLS-1$ |
| } |
| this.writer= writer; |
| } |
| |
| |
| public void setMaxFragmentSize(final int size) { |
| this.maxFragmentSize= size; |
| } |
| |
| public void print(final TreePartitionNode node, final IDocument document) throws IOException { |
| this.document= document; |
| try { |
| print(node, 0); |
| } |
| finally { |
| this.document= null; |
| } |
| } |
| |
| public void print(final List<TreePartition> partitions, final IDocument document) throws IOException { |
| this.document= document; |
| try { |
| print(partitions); |
| } |
| finally { |
| this.document= null; |
| } |
| } |
| |
| protected void print(final TreePartitionNode node, final int nodeDepth) throws IOException { |
| printIdent(nodeDepth); |
| |
| final int startOffset= node.getStartOffset(); |
| final int endOffset= node.getEndOffset(); |
| |
| this.writer.write('['); |
| this.writer.write(Integer.toString(startOffset)); |
| this.writer.write(", "); //$NON-NLS-1$ |
| this.writer.write(Integer.toString(endOffset)); |
| this.writer.write(") "); //$NON-NLS-1$ |
| this.writer.write(node.getType().toString()); |
| printFragment(startOffset, endOffset); |
| this.writer.write('\n'); |
| |
| final int childCount= node.getChildCount(); |
| for (int childIdx= 0; childIdx < childCount; childIdx++) { |
| print(node.getChild(childIdx), nodeDepth + 1); |
| } |
| } |
| |
| protected void print(final List<TreePartition> partitions) throws IOException { |
| for (int i= 0; i < partitions.size(); i++) { |
| final TreePartition partition= partitions.get(i); |
| final int startOffset= partition.getOffset(); |
| final int endOffset= partition.getEndOffset(); |
| |
| this.writer.write('['); |
| this.writer.write(Integer.toString(startOffset)); |
| this.writer.write(", "); //$NON-NLS-1$ |
| this.writer.write(Integer.toString(endOffset)); |
| this.writer.write(") "); //$NON-NLS-1$ |
| this.writer.write(partition.getType()); |
| printFragment(startOffset, endOffset); |
| this.writer.append('\n'); |
| } |
| } |
| |
| protected void printIdent(final int depth) throws IOException { |
| for (int i= 0; i < depth; i++) { |
| this.writer.write(" "); //$NON-NLS-1$ |
| } |
| } |
| |
| protected void printFragment(final int startOffset, final int endOffset) throws IOException { |
| final IDocument document= this.document; |
| if (document != null && this.maxFragmentSize > 0) { |
| try { |
| this.writer.write(": "); //$NON-NLS-1$ |
| int l= endOffset - startOffset; |
| if (l <= this.maxFragmentSize) { |
| writeEncoded(document.get(startOffset, l)); |
| } |
| else if (this.maxFragmentSize < 13) { |
| writeEncoded(document.get(startOffset, this.maxFragmentSize - 3)); |
| this.writer.write(" ... "); //$NON-NLS-1$ |
| } |
| else { |
| l= (this.maxFragmentSize - 3) / 2; |
| writeEncoded(document.get(startOffset, l)); |
| this.writer.write(" ... "); //$NON-NLS-1$ |
| writeEncoded(document.get(endOffset - l, l)); |
| } |
| } |
| catch (final BadLocationException e) { |
| this.writer.write("!!!ERROR!!!"); //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| private void writeEncoded(final String s) throws IOException { |
| for (int i= 0; i < s.length(); i++) { |
| final int c= s.charAt(i); |
| if (c < 0x10) { |
| this.writer.write("<0x0"); //$NON-NLS-1$ |
| this.writer.write(Integer.toHexString(c)); |
| this.writer.write('>'); |
| } |
| else if (c < 0x20) { |
| this.writer.write("<0x"); //$NON-NLS-1$ |
| this.writer.write(Integer.toHexString(c)); |
| this.writer.write('>'); |
| } |
| else { |
| this.writer.write(c); |
| } |
| } |
| } |
| |
| } |
| |
| |
| public final static TreePartitionNode getRootNode(final IDocument document, final String partitioning) { |
| try { |
| final TreePartition partition= (TreePartition) TextUtilities.getPartition(document, partitioning, |
| 0, false ); |
| TreePartitionNode node= partition.getTreeNode(); |
| TreePartitionNode parent; |
| while ((parent= node.getParent()) != null) { |
| node= parent; |
| } |
| return node; |
| } |
| catch (final BadLocationException e) { |
| throw new IllegalStateException(e); |
| } |
| } |
| |
| public final static @Nullable TreePartitionNode getNode(final IDocument document, final String partitioning, |
| final int offset, final boolean prefereOpen) throws BadLocationException { |
| final TreePartition partition= (TreePartition) TextUtilities.getPartition(document, |
| partitioning, offset, prefereOpen ); |
| if (partition instanceof TreePartition) { |
| return partition.getTreeNode(); |
| } |
| return null; |
| } |
| |
| public final static @Nullable TreePartitionNode searchNodeUp(@Nullable TreePartitionNode node, |
| final TreePartitionNodeType type) { |
| while (node != null && type != node.getType()) { |
| node= node.getParent(); |
| } |
| return node; |
| } |
| |
| public final static @Nullable TreePartitionNode searchNodeUp(@Nullable TreePartitionNode node, |
| final String partitionType) { |
| while (node != null && partitionType != node.getType().getPartitionType()) { |
| node= node.getParent(); |
| } |
| return node; |
| } |
| |
| public final static @Nullable TreePartitionNode searchNodeUp(@Nullable TreePartitionNode node, |
| final PartitionConstraint partitionConstraint) { |
| while (node != null && !partitionConstraint.matches(node.getType().getPartitionType())) { |
| node= node.getParent(); |
| } |
| return node; |
| } |
| |
| public final static @Nullable TreePartitionNode searchNode(final IDocument document, final String partitioning, |
| final int offset, final boolean prefereOpen, final TreePartitionNodeType type) |
| throws BadLocationException { |
| final TreePartition partition= (TreePartition)TextUtilities.getPartition(document, partitioning, |
| offset, prefereOpen ); |
| return searchNodeUp(partition.getTreeNode(), type); |
| } |
| |
| public final static @Nullable TreePartitionNode searchNode(final IDocument document, final String partitioning, |
| final int offset, final boolean prefereOpen, final String partitionType) |
| throws BadLocationException { |
| final TreePartition partition= (TreePartition) TextUtilities.getPartition(document, partitioning, |
| offset, prefereOpen ); |
| return searchNodeUp(partition.getTreeNode(), partitionType); |
| } |
| |
| public final static @Nullable TreePartitionNode searchNode(final IDocument document, final String partitioning, |
| final int offset, final boolean prefereOpen, final PartitionConstraint partitionConstraint) |
| throws BadLocationException { |
| final TreePartition partition= (TreePartition) TextUtilities.getPartition(document, partitioning, |
| offset, prefereOpen ); |
| return searchNodeUp(partition.getTreeNode(), partitionConstraint); |
| } |
| |
| public final static @Nullable TextRegion searchPartitionRegion(final IDocument document, final String partitioning, |
| final int offset, final boolean prefereOpen, final PartitionConstraint partitionConstraint) |
| throws BadLocationException { |
| return searchPartitionRegion((TreePartition) TextUtilities.getPartition(document, partitioning, |
| offset, prefereOpen ), partitionConstraint ); |
| } |
| |
| public final static @Nullable TextRegion searchPartitionRegion(final TreePartition partition, |
| final PartitionConstraint partitionConstraint) { |
| if (partition == null) { |
| throw new NullPointerException("partition"); |
| } |
| if (!partitionConstraint.matches(partition.getType())) { |
| return null; |
| } |
| final int begin= searchBegin(partition.getTreeNode(), partition.getOffset(), partitionConstraint); |
| final int end= searchEnd(partition.getTreeNode(), partition.getEndOffset(), partitionConstraint); |
| return new BasicTextRegion(begin, end); |
| } |
| |
| private static int searchBegin(final TreePartitionNode node, final int offset, |
| final PartitionConstraint partitionConstraint) { |
| final int childCount= node.getChildCount(); |
| int childIdx= node.indexOfChild(offset); |
| if (childIdx < 0) { |
| childIdx= -(childIdx + 1); |
| } |
| if (childIdx == childCount) { |
| childIdx--; |
| } |
| for (; childIdx >= 0; childIdx--) { |
| final TreePartitionNode child= node.getChild(childIdx); |
| if (!partitionConstraint.matches(child.getType().getPartitionType())) { |
| return child.getEndOffset(); |
| } |
| final int stop= searchBeginChild(node, partitionConstraint); |
| if (stop >= 0) { |
| return stop; |
| } |
| } |
| final TreePartitionNode parent= node.getParent(); |
| if (parent == null |
| || !partitionConstraint.matches(parent.getType().getPartitionType())) { |
| return node.getStartOffset(); |
| } |
| return searchBegin(parent, offset, partitionConstraint); |
| } |
| |
| private static int searchBeginChild(final TreePartitionNode node, |
| final PartitionConstraint partitionConstraint) { |
| final int childCount= node.getChildCount(); |
| for (int childIdx= childCount - 1; childIdx >= 0; childIdx--) { |
| final TreePartitionNode child= node.getChild(childIdx); |
| if (!partitionConstraint.matches(child.getType().getPartitionType())) { |
| return child.getEndOffset(); |
| } |
| final int stop= searchBeginChild(child, partitionConstraint); |
| if (stop >= 0) { |
| return stop; |
| } |
| } |
| return -1; |
| } |
| |
| private static int searchEnd(final TreePartitionNode node, final int offset, |
| final PartitionConstraint partitionConstraint) { |
| final int childCount= node.getChildCount(); |
| int childIdx= (offset == node.getStartOffset()) ? 0 : node.indexOfChild(offset); |
| if (childIdx < 0) { |
| childIdx= -(childIdx + 1); |
| } |
| for (; childIdx < childCount; childIdx++) { |
| final TreePartitionNode child= node.getChild(childIdx); |
| if (!partitionConstraint.matches(child.getType().getPartitionType())) { |
| return child.getStartOffset(); |
| } |
| final int stop= searchEndChild(node, partitionConstraint); |
| if (stop >= 0) { |
| return stop; |
| } |
| } |
| final TreePartitionNode parent= node.getParent(); |
| if (parent == null |
| || !partitionConstraint.matches(parent.getType().getPartitionType())) { |
| return node.getEndOffset(); |
| } |
| return searchEnd(parent, offset, partitionConstraint); |
| } |
| |
| private static int searchEndChild(final TreePartitionNode node, |
| final PartitionConstraint partitionConstraint) { |
| final int childCount= node.getChildCount(); |
| for (int childIdx= 0; childIdx < childCount; childIdx++) { |
| final TreePartitionNode child= node.getChild(childIdx); |
| if (!partitionConstraint.matches(child.getType().getPartitionType())) { |
| return child.getStartOffset(); |
| } |
| final int stop= searchEndChild(child, partitionConstraint); |
| if (stop >= 0) { |
| return stop; |
| } |
| } |
| return -1; |
| } |
| |
| |
| private TreePartitionUtils() {} |
| |
| } |