| /*=============================================================================# |
| # Copyright (c) 2009, 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.model.core.build; |
| |
| import static org.eclipse.statet.jcommons.lang.ObjectUtils.nonNullAssert; |
| |
| import java.io.StringWriter; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.IdentityHashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.runtime.MultiStatus; |
| |
| import org.eclipse.statet.jcommons.collections.ImCollection; |
| import org.eclipse.statet.jcommons.collections.ImCollections; |
| import org.eclipse.statet.jcommons.collections.ImIdentitySet; |
| import org.eclipse.statet.jcommons.collections.ImList; |
| 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.ast.core.AstInfo; |
| import org.eclipse.statet.ltk.ast.core.EmbeddingAstNode; |
| import org.eclipse.statet.ltk.ast.core.util.AstPrinter; |
| import org.eclipse.statet.ltk.core.source.SourceConfig; |
| import org.eclipse.statet.ltk.core.source.SourceContent; |
| import org.eclipse.statet.ltk.model.core.ModelManager; |
| import org.eclipse.statet.ltk.model.core.element.WorkspaceSourceUnit; |
| import org.eclipse.statet.ltk.project.core.LtkProject; |
| |
| |
| @NonNullByDefault |
| public abstract class ExtensibleReconciler< |
| TProject extends LtkProject, |
| TModelContainer extends SourceUnitModelContainer<?, ?>, |
| TEmbeddedModelReconciler extends SourceUnitEmbeddedModelReconciler<TProject, ?>> { |
| |
| |
| protected static final class ExtensionData< |
| TProject extends LtkProject, |
| TConfig extends ReconcileConfig<?>, |
| TReconciler extends SourceUnitEmbeddedModelReconciler<TProject, TConfig>> { |
| |
| public final String modelTypeId; |
| |
| public final TReconciler reconciler; |
| |
| public final TConfig config; |
| |
| |
| public ExtensionData(final TReconciler reconciler, |
| final TConfig config) { |
| this.modelTypeId= reconciler.getModelTypeId(); |
| this.reconciler= reconciler; |
| this.config= nonNullAssert(config); |
| } |
| |
| |
| public static <T extends ExtensionData<?, ?, ?>> boolean contains(final ImList<T> list, final String modelTypeId) { |
| for (final var data : list) { |
| if (data.modelTypeId == modelTypeId) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public static <T extends ExtensionData<?, ?, ?>> @Nullable T get(final ImList<T> list, final String modelTypeId) { |
| for (final var data : list) { |
| if (data.modelTypeId == modelTypeId) { |
| return data; |
| } |
| } |
| return null; |
| } |
| |
| } |
| |
| private static class ExtensionItem< |
| TProject extends LtkProject, |
| TConfig extends ReconcileConfig<?>, |
| TReconciler extends SourceUnitEmbeddedModelReconciler<TProject, TConfig>> { |
| |
| private final String modelTypeId; |
| |
| private final @Nullable TReconciler reconciler; |
| |
| private @Nullable ExtensionData<TProject, TConfig, TReconciler> projectData; |
| |
| public ExtensionItem(final String modelTypeId, @Nullable final TReconciler reconciler) { |
| this.modelTypeId= modelTypeId; |
| this.reconciler= reconciler; |
| } |
| |
| |
| public void clear() { |
| this.projectData= null; |
| } |
| |
| public ExtensionData<TProject, TConfig, TReconciler> init(final TProject wsProject) { |
| var projectData= this.projectData; |
| if (projectData == null) { |
| projectData= createData(wsProject.getProject(), wsProject, |
| ModelManager.MODEL_DEPENDENCIES | ModelManager.RECONCILE ); |
| this.projectData= projectData; |
| } |
| return projectData; |
| } |
| |
| public ExtensionData<TProject, TConfig, TReconciler> createData( |
| final @Nullable IProject wsProject, final @Nullable TProject ltkProject, |
| final int level) { |
| @SuppressWarnings({ "null" }) |
| final TReconciler reconciler= this.reconciler; |
| return new ExtensionData<>(reconciler, |
| reconciler.createConfig(wsProject, ltkProject, level) ); |
| } |
| |
| } |
| |
| |
| protected boolean stop= false; |
| |
| private final Map<String, ExtensionItem<TProject, ?, TEmbeddedModelReconciler>> extensions= new HashMap<>(); |
| |
| private @Nullable TProject project; |
| private @Nullable MultiStatus statusCollector; |
| |
| private @Nullable AstPrinter raDebugAstPrinter; |
| |
| |
| public ExtensibleReconciler() { |
| } |
| |
| |
| public void init(final TProject project, final MultiStatus statusCollector) { |
| this.project= nonNullAssert(project); |
| this.statusCollector= statusCollector; |
| |
| synchronized (this.extensions) { |
| for (final var extension : this.extensions.values()) { |
| extension.clear(); |
| } |
| } |
| } |
| |
| |
| public @Nullable MultiStatus getStatusCollector() { |
| return this.statusCollector; |
| } |
| |
| |
| protected abstract @Nullable TProject getProject(IProject wsProject); |
| |
| protected abstract @Nullable TEmbeddedModelReconciler createEmbeddedModelReconciler( |
| final String modelTypeId); |
| |
| protected @Nullable ExtensionItem<TProject, ?, TEmbeddedModelReconciler> getExtension( |
| final String modelTypeId) { |
| synchronized (this.extensions) { |
| var item= this.extensions.get(modelTypeId); |
| if (item== null) { |
| item= new ExtensionItem(modelTypeId, createEmbeddedModelReconciler(modelTypeId)); |
| this.extensions.put(modelTypeId, item); |
| } |
| return (item.reconciler != null) ? item : null; |
| } |
| } |
| |
| protected ImList<ExtensionData<TProject, ?, TEmbeddedModelReconciler>> initExtensions( |
| final ImCollection<String> modelTypeIds, |
| final TModelContainer adapter, final int flags) { |
| final var items= new ArrayList<ExtensionItem<TProject, ?, TEmbeddedModelReconciler>>(); |
| for (final String modelTypeId : modelTypeIds) { |
| final var extension= getExtension(modelTypeId); |
| if (extension != null) { |
| items.add(extension); |
| } |
| } |
| |
| @SuppressWarnings("unchecked") |
| final @NonNull ExtensionData<TProject, ?, TEmbeddedModelReconciler>[] dataArray= new @NonNull ExtensionData[items.size()]; |
| var ltkProject= this.project; |
| if (ltkProject != null) { |
| for (int i= 0; i < dataArray.length; i++) { |
| dataArray[i]= items.get(i).init(ltkProject); |
| } |
| } |
| else { |
| final var sourceUnit= adapter.getSourceUnit(); |
| final var wsProject= (sourceUnit instanceof WorkspaceSourceUnit) ? |
| ((WorkspaceSourceUnit)sourceUnit).getResource().getProject() : null; |
| ltkProject= (wsProject != null) ? getProject(wsProject) : null; |
| for (int i= 0; i < dataArray.length; i++) { |
| dataArray[i]= items.get(i).createData(wsProject, ltkProject, flags); |
| } |
| } |
| return ImCollections.newList(dataArray); |
| } |
| |
| |
| protected ImList<SourceConfig> createSourceConfig(final SourceConfig mainSourceConfig, |
| final ImList<ExtensionData<TProject, ?, TEmbeddedModelReconciler>> extensions) { |
| final var configArray= new SourceConfig[1 + extensions.size()]; |
| int i= 0; |
| configArray[i++]= mainSourceConfig; |
| for (final var extensionData : extensions) { |
| configArray[i++]= extensionData.config.getSourceConfig(); |
| } |
| return ImCollections.newList(configArray); |
| } |
| |
| protected ImIdentitySet<String> collectEmbeddedTypeIds(final List<? extends EmbeddingAstNode> nodes) { |
| final Map<String, Boolean> ids= new IdentityHashMap<>(4); |
| for (final EmbeddingAstNode node : nodes) { |
| ids.put(node.getForeignTypeId(), Boolean.TRUE); |
| } |
| return ImCollections.toIdentitySet(ids.keySet()); |
| } |
| |
| |
| protected void logAst(final AstInfo ast, final SourceContent content) { |
| AstPrinter printer= this.raDebugAstPrinter; |
| if (printer == null) { |
| printer= new AstPrinter(new StringWriter()); |
| this.raDebugAstPrinter= printer; |
| } |
| final StringWriter out= (StringWriter)printer.getWriter(); |
| out.getBuffer().setLength(0); |
| try { |
| out.append("====\n" + getClass().getSimpleName() + " AST:\n"); //$NON-NLS-1$ |
| printer.print(ast.getRoot(), content.getString()); |
| out.append("====\n"); //$NON-NLS-1$ |
| System.out.println(out.toString()); |
| } |
| catch (final Exception e) { |
| System.out.println(out.toString()); |
| e.printStackTrace(); |
| } |
| } |
| |
| } |