| /*=============================================================================# |
| # Copyright (c) 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.ltk.ui.util; |
| |
| import java.util.ArrayList; |
| import java.util.IdentityHashMap; |
| import java.util.List; |
| |
| import org.eclipse.jface.viewers.ITreeContentProvider; |
| import org.eclipse.jface.viewers.Viewer; |
| |
| import org.eclipse.statet.jcommons.lang.Disposable; |
| 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.ltk.core.source.SourceModelStamp; |
| import org.eclipse.statet.ltk.model.core.LtkModelUtils; |
| import org.eclipse.statet.ltk.model.core.LtkModels; |
| import org.eclipse.statet.ltk.model.core.element.EmbeddingForeignElement; |
| 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.SourceStructElement; |
| import org.eclipse.statet.ltk.model.core.element.SourceUnitModelInfo; |
| import org.eclipse.statet.ltk.ui.SourceStructContentProvider; |
| |
| |
| @NonNullByDefault |
| public class ExtModelContentProvider implements ITreeContentProvider { |
| |
| |
| private static final @NonNull SourceStructElement<?, ?>[] NO_CHILDREN= new SourceStructElement[0]; |
| |
| protected static final SourceStructContentProvider NO_PROVIDER= new SourceStructContentProvider() { |
| }; |
| |
| |
| private final IdentityHashMap<String, SourceStructContentProvider> providers= new IdentityHashMap<>(4); |
| |
| |
| public interface ModelContent { |
| |
| @Nullable SourceUnitModelInfo getModelInfo(Object inputElement); |
| |
| @Nullable LtkModelElementFilter<? super SourceStructElement<?, ?>> getContentFilter(); |
| |
| } |
| |
| |
| private final ModelContent content; |
| |
| private final ArrayList<SourceStructElement<?, ?>> filteredChildren= new ArrayList<>(); |
| |
| |
| public ExtModelContentProvider(final ModelContent content) { |
| this.content= content; |
| } |
| |
| @Override |
| public void dispose() { |
| for (final var provider : this.providers.values()) { |
| if (provider instanceof Disposable) { |
| ((Disposable)provider).dispose(); |
| } |
| } |
| this.providers.clear(); |
| } |
| |
| |
| protected final ModelContent getContent() { |
| return this.content; |
| } |
| |
| public @Nullable SourceModelStamp getStamp(final Object inputElement) { |
| final SourceUnitModelInfo modelInfo= getContent().getModelInfo(inputElement); |
| return (modelInfo != null) ? modelInfo.getStamp() : null; |
| } |
| |
| @Override |
| public void inputChanged(final Viewer viewer, final @Nullable Object oldInput, final @Nullable Object newInput) { |
| } |
| |
| |
| protected @Nullable SourceStructContentProvider getSourceStructContentProvider(final @Nullable String modelId) { |
| if (modelId == null) { |
| return null; |
| } |
| var provider= this.providers.get(modelId); |
| if (provider == null) { |
| provider= LtkModels.getModelAdapter(modelId, SourceStructContentProvider.class); |
| if (provider == null) { |
| provider= NO_PROVIDER; |
| } |
| this.providers.put(modelId, provider); |
| } |
| return (provider != NO_PROVIDER) ? provider : null; |
| } |
| |
| protected void addProvider(final String modelId, final SourceStructContentProvider provider) { |
| this.providers.put(modelId, provider); |
| } |
| |
| |
| private @NonNull SourceStructElement<?, ?>[] filter( |
| final List<? extends SourceStructElement<?, ?>> elements, |
| final @Nullable LtkModelElementFilter<? super SourceStructElement<?, ?>> filter) { |
| if (elements.isEmpty()) { |
| return NO_CHILDREN; |
| } |
| if (filter == null) { |
| return elements.toArray(new @NonNull SourceStructElement[elements.size()]); |
| } |
| try { |
| for (final var element : elements) { |
| if (filter.include(element)) { |
| this.filteredChildren.add(element); |
| } |
| } |
| return this.filteredChildren.toArray(new @NonNull SourceStructElement[elements.size()]); |
| } |
| finally { |
| this.filteredChildren.clear(); |
| } |
| } |
| |
| |
| private boolean skipToForeignElement(final @Nullable SourceStructElement<?, ?> element) { |
| return (element != null |
| && (element.getElementType() & LtkModelElement.MASK_C1) == LtkModelElement.C1_EMBEDDED ); |
| } |
| |
| protected boolean skipInTree(final SourceStructElement<?, ?> element) { |
| return false; |
| } |
| |
| protected @NonNull SourceStructElement<?, ?>[] getElements(final @Nullable SourceUnitModelInfo modelInfo) { |
| if (modelInfo != null) { |
| final var children= modelInfo.getSourceElement() |
| .getSourceChildren(getContent().getContentFilter()); |
| return children.toArray(new @NonNull SourceStructElement[children.size()]); |
| } |
| return NO_CHILDREN; |
| } |
| |
| protected @Nullable SourceStructElement<?, ?> getParent(final SourceStructElement<?, ?> element) { |
| var parent= element.getSourceParent(); |
| while (parent != null && (skipToForeignElement(parent) || skipInTree(parent))) { |
| parent= parent.getSourceParent(); |
| } |
| return parent; |
| } |
| |
| protected boolean hasChildren(@Nullable SourceStructElement<?, ?> element) { |
| if (skipToForeignElement(element)) { |
| element= ((EmbeddingForeignElement<?, ?>)element).getForeignElement(); |
| } |
| SourceStructContentProvider provider; |
| if (element != null) { |
| if ((provider= getSourceStructContentProvider(element.getModelTypeId())) != null) { |
| return provider.hasSourceChildren(element, getContent().getContentFilter()); |
| } |
| else { |
| return element.hasSourceChildren(getContent().getContentFilter()); |
| } |
| } |
| return false; |
| } |
| |
| protected @NonNull SourceStructElement<?, ?>[] getChildren(@Nullable SourceStructElement<?, ?> element) { |
| if (skipToForeignElement(element)) { |
| element= ((EmbeddingForeignElement<?, ?>)element).getForeignElement(); |
| } |
| SourceStructContentProvider provider; |
| if (element != null) { |
| try { |
| if ((provider= getSourceStructContentProvider(element.getModelTypeId())) != null) { |
| final var children= provider.getSourceChildren(element, |
| getContent().getContentFilter(), this.filteredChildren ); |
| return children.toArray(new @NonNull SourceStructElement[children.size()]); |
| } |
| else { |
| final var children= LtkModelUtils.filter(element.getSourceChildren(null), |
| getContent().getContentFilter(), this.filteredChildren ); |
| return children.toArray(new @NonNull SourceStructElement[children.size()]); |
| } |
| } |
| finally { |
| this.filteredChildren.clear(); |
| } |
| } |
| return NO_CHILDREN; |
| } |
| |
| |
| @Override |
| public @NonNull Object[] getElements(final Object inputElement) { |
| return getElements(getContent().getModelInfo(inputElement)); |
| } |
| |
| @Override |
| public @Nullable Object getParent(final Object element) { |
| if (element instanceof SourceStructElement) { |
| return getParent((SourceStructElement<?, ?>)element); |
| } |
| return null; |
| } |
| |
| @Override |
| public boolean hasChildren(final Object element) { |
| if (element instanceof SourceStructElement) { |
| return hasChildren((SourceStructElement<?, ?>)element); |
| } |
| return false; |
| } |
| |
| @Override |
| public @NonNull Object[] getChildren(final Object element) { |
| if (element instanceof SourceStructElement) { |
| return getChildren((SourceStructElement<?, ?>)element); |
| } |
| return NO_CHILDREN; |
| } |
| |
| } |