blob: 1851b6676c972891b2aa65dbeebb1f5737ab5041 [file] [log] [blame]
/*=============================================================================#
# 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();
}
}
}