blob: 50780530076e22fe994830d2884725b33b7f1fa6 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2009, 2020 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.ltk.model.core;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.statet.jcommons.lang.NonNull;
import org.eclipse.statet.jcommons.lang.NonNullByDefault;
import org.eclipse.statet.jcommons.lang.Nullable;
import org.eclipse.statet.jcommons.text.core.TextRegion;
import org.eclipse.statet.ltk.model.core.element.LtkModelElement;
import org.eclipse.statet.ltk.model.core.element.LtkModelElementFilter;
import org.eclipse.statet.ltk.model.core.element.SourceElement;
import org.eclipse.statet.ltk.model.core.element.SourceStructElement;
import org.eclipse.statet.ltk.model.core.element.SourceUnit;
@NonNullByDefault
public final class LtkModelUtils {
public static @Nullable LtkModelElement<?> getModelElement(final @Nullable Object element) {
if (element instanceof LtkModelElement) {
return (LtkModelElement<?>)element;
}
if (element instanceof IAdaptable) {
return ((IAdaptable)element).getAdapter(LtkModelElement.class);
}
return null;
}
public static @Nullable SourceUnit getSourceUnit(final @Nullable LtkModelElement<?> element) {
if (element instanceof SourceUnit) {
return (SourceUnit)element;
}
if (element instanceof SourceElement) {
return ((SourceElement<?>)element).getSourceUnit();
}
return null;
}
public static final <T> boolean hasChildren(final List<? extends @NonNull T> children, final @Nullable LtkModelElementFilter<? super @NonNull T> filter) {
if (filter == null) {
return (!children.isEmpty());
}
else {
for (final T child : children) {
if (filter.include(child)) {
return true;
}
}
return false;
}
}
public static final <T> List<? extends T> getChildren(final List<? extends @NonNull T> children, final @Nullable LtkModelElementFilter<? super @NonNull T> filter) {
if (filter == null) {
return children;
}
else {
final ArrayList<T> filtered= new ArrayList<>(children.size());
for (final T child : children) {
if (filter.include(child)) {
filtered.add(child);
}
}
return filtered;
}
}
public static @Nullable SourceStructElement getCoveringSourceElement(final SourceStructElement root,
final TextRegion region) {
return getCoveringSourceElement(root, region.getStartOffset(), region.getEndOffset());
}
public static @Nullable SourceStructElement getCoveringSourceElement(final SourceStructElement root,
final int startOffset, final int endOffset) {
SourceStructElement ok= root;
CHECK: while (ok != null) {
final List<? extends SourceStructElement> children= ok.getSourceChildren(null);
for (final SourceStructElement child : children) {
final TextRegion sourceRange= child.getSourceRange();
final TextRegion docRange= child.getDocumentationRange();
final int childOffset= (docRange != null) ?
Math.min(sourceRange.getStartOffset(), docRange.getStartOffset()) :
sourceRange.getStartOffset();
if (startOffset >= childOffset) {
final int childEnd= (docRange != null) ?
Math.max(sourceRange.getEndOffset(), docRange.getEndOffset()) :
sourceRange.getEndOffset();
if ((startOffset < endOffset) ?
(endOffset <= childEnd) : (endOffset < childEnd)) {
ok= child;
continue CHECK;
}
}
else {
break CHECK;
}
}
break CHECK;
}
return ok;
}
public static int searchCoveringSourceElement(final List<? extends SourceStructElement> elements,
final int offset) {
// binary search
int low= 0;
int high= elements.size() - 1;
while (low <= high) {
final int mid= (low + high) >> 1;
final TextRegion region= elements.get(mid).getSourceRange();
if (region.getEndOffset() < offset) {
low= mid + 1;
}
else if (region.getStartOffset() > offset) {
high= mid - 1;
}
else {
return mid;
}
}
return -(low + 1);
}
public static <T extends SourceStructElement> @Nullable T getCoveringSourceElement(final List<T> elements,
final int offset) {
final int idx= searchCoveringSourceElement(elements, offset);
if (idx >= 0) {
return elements.get(idx);
}
return null;
}
}