blob: 62f563dd8bad6bef580cb8a38cc7026dea7d89a8 [file] [log] [blame]
/**
*
* Copyright (c) 2011, 2016 - Loetz GmbH&Co.KG (69115 Heidelberg, Germany)
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Christophe Loetz (Loetz GmbH&Co.KG) - initial implementation
*/
package org.eclipse.osbp.ide.core.ui.softwarefactory.builder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.CopyOnWriteArrayList;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.ProgressMonitorWrapper;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.common.util.WrappedException;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.osbp.ide.core.ui.softwarefactory.constants.Constants;
import org.eclipse.osbp.ide.core.ui.softwarefactory.extender.IModelExtenderProvider;
import org.eclipse.osbp.ide.core.ui.softwarefactory.extender.IModelExtenderProvider.Event.Listener;
import org.eclipse.osbp.ide.core.ui.softwarefactory.extender.ModelExtendedEvent;
import org.eclipse.osbp.ide.core.ui.softwarefactory.extender.ModelExtenderUtils;
import org.eclipse.osbp.ide.core.ui.softwarefactory.extender.ModelInstanceDescription;
import org.eclipse.xtext.resource.XtextResourceSet;
import org.eclipse.xtext.ui.editor.findrefs.IReferenceFinder;
import org.eclipse.xtext.ui.resource.XtextResourceSetProvider;
import org.eclipse.xtext.util.concurrent.IUnitOfWork;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.Inject;
public class OSBP2Builder extends IncrementalProjectBuilder {
public static final String BUILDER_ID = Constants.BUILDER_ID;
private static final Logger LOGGER = LoggerFactory.getLogger(OSBP2Builder.class);
public static final String MODELS_BASE_SRC_DIRECTORY = "models";
public static final String SOURCE_BASE_SRC_DIRECTORY = "src";
public static final String ENTITY_MODEL_EXTENSION = "entity";
public static final String AUTHORIZATION_MODEL_EXTENSION = "authorization";
public static final String DATAMART_MODEL_EXTENSION = "datamart";
public static final String DATAINTERCHANGE_MODEL_EXTENSION = "data";
public static final String DTO_MODEL_EXTENSION = "dto";
public static final String SERVICE_MODEL_EXTENSION = "service";
public static final String ACTION_MODEL_EXTENSION = "action";
public static final String TABLE_MODEL_EXTENSION = "table";
public static final String DIALOG_MODEL_EXTENSION = "dialog";
public static final String UIMODEL_MODEL_EXTENSION = "ui";
public static final String PERSPECTIVE_MODEL_EXTENSION = "perspective";
public static final String MENU_MODEL_EXTENSION = "menu";
@SuppressWarnings("serial")
private Map<String, List<String>> dependencyTree = new HashMap<String, List<String>>() {{ //NOSONAR
put("datatype", new ArrayList<String>() {{ // NOSONAR
add("entity");
add("dialog");
}});
put("entity", new ArrayList<String>() {{ // NOSONAR
add("datamart");
add("datainterchange");
}});
put("dto", new ArrayList<String>() {{ // NOSONAR
add("dialog");
add("ui");
add("statemachine");
add("functionlibrary");
add("entitymock");
add("blip");
}});
put("datamart", new ArrayList<String>() {{ // NOSONAR
add("report");
add("table");
add("chart");
}});
put("data", new ArrayList<String>() {{ // NOSONAR
add("action");
add("entitymock");
}});
put("functionlibrary", new ArrayList<String>() {{ // NOSONAR
add("action");
add("statemachine");
add("blip");
}});
put("authorization", new ArrayList<String>() {{ // NOSONAR
add("dialog");
}});
put("message", new ArrayList<String>() {{ // NOSONAR
add("action");
}});
}};
@Inject
private XtextResourceSetProvider rsProvider;
@Inject
private IModelExtenderProvider.Event.Source modelExtenderBuilder;
@Inject
private IWorkspace workspace;
private boolean firstBuild = true;
@Override
protected IProject[] build(int kind, Map<String, String> args, IProgressMonitor monitor) throws CoreException {
SubMonitor progress = null;
boolean isModelExtending = false;
List<String> extensionsRebuilt = new CopyOnWriteArrayList<>();
for (String fileExtension : modelExtenderBuilder.getPending()) {
for (Listener listener : modelExtenderBuilder.getListenerList()) {
Map<String, ModelInstanceDescription> modelInstanceDecriptionMap = modelExtenderBuilder
.getModelInstanceDescriptionMap(listener);
isModelExtending = true;
for (Entry<String, ModelInstanceDescription> extension : modelInstanceDecriptionMap.entrySet()) {
if (extension.getKey().equals(fileExtension)
&& extension.getValue().getProject().equals(getProject())
&& extension.getValue().isDone()
&& !extension.getValue().isBuilt()) {
LOGGER.debug("rebuild for {}", extension.getKey());
extension.getValue().setBuilt(true);
needRebuild();
extensionsRebuilt.add(extension.getKey());
modelExtenderBuilder.getPending().remove(fileExtension);
}
}
}
}
if (!isModelExtending) { // this disturbs the model extender workflow
try {
final String taskName = "Building" + getProject().getName() + ": "; //$NON-NLS-2$
progress = SubMonitor.convert(new ProgressMonitorWrapper(monitor) { // NOSONAR
// -
// lambda
@Override
public void subTask(String name) { // NOSONAR
super.subTask(taskName + name);
}
}, 8);
if (kind == FULL_BUILD || firstBuild) {
firstBuild = false;
fullBuild(progress.newChild(1));
} else {
IResourceDelta delta = getDelta(getProject());
if (delta == null || isOpened(delta)) {
fullBuild(progress.newChild(1));
} else {
incrementalBuild(delta, progress.newChild(1));
}
}
progress.worked(8);
} catch (CoreException e) {
LOGGER.error(e.getMessage(), e);
throw e;
} catch (OperationCanceledException e) {
forgetLastBuiltState();
throw e;
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
} finally {
if (progress != null) {
progress.done();
}
}
}
if (!extensionsRebuilt.isEmpty()) {
for (String extension : extensionsRebuilt) {
LOGGER.debug("extending finished for {}", extension);
modelExtenderBuilder.notifyListeners(new ModelExtendedEvent(extension));
}
}
return getProject().getReferencedProjects();
}
private boolean checkFolder(IFolder folder) {
return (MODELS_BASE_SRC_DIRECTORY.equals(folder.getName()) || SOURCE_BASE_SRC_DIRECTORY.equals(folder.getName()));
}
private boolean checkFile(IFile file) {
return buildUpstream(file.getFileExtension());
}
private boolean buildUpstream(String extension) {
if(!dependencyTree.containsKey(extension)) {
return false;
}
Map<URI, IProject> result = new LinkedHashMap<>();
iterateDeps(extension, result);
for(Entry<URI, IProject> res:result.entrySet()) {
XtextResourceSet resourceSet = (XtextResourceSet) rsProvider.get(res.getValue());
Resource resource = resourceSet.getResource(res.getKey(), true);
ModelExtenderUtils.writeResource(resource, res.getValue().getName());
}
return true;
}
private void iterateDeps(String extension, Map<URI, IProject> result) {
if(dependencyTree.containsKey(extension)) {
for(String depExt:dependencyTree.get(extension)) {
iterateDeps(depExt, result);
getProjectsByExtension(depExt, result);
}
}
}
private void getProjectsByExtension(String extension, Map<URI, IProject> result) {
for(IProject project:workspace.getRoot().getProjects()) {
if(project.isOpen() && extension.equals(project.getFileExtension())) {
try {
project.accept(new IResourceVisitor() {
@Override
public boolean visit(IResource resource) throws CoreException {
if (resource.getType() == IResource.FILE) {
IFile file = (IFile) resource;
if (extension.equalsIgnoreCase(file.getFileExtension()) && !file.getFullPath().toString().contains("target/classes")) {
result.put(URI.createPlatformPluginURI(file.getFullPath().toString(), false), project);
}
}
return true;
}
});
} catch (CoreException e) {
LOGGER.error("{}", e);
}
break;
}
}
}
/**
* @param monitor
* the progress monitor to use for reporting progress to the
* user. It is the caller's responsibility to call done() on the
* given monitor. Accepts null, indicating that no progress
* should be reported and that the operation cannot be cancelled.
*/
protected void incrementalBuild(IResourceDelta delta, final IProgressMonitor monitor) throws CoreException {
final SubMonitor progress = SubMonitor.convert(monitor, "Updating references", 4);
progress.subTask("Updating references");
if (progress.isCanceled())
throw new OperationCanceledException();
progress.worked(2);
IProject project = getProject();
if (!project.getDescription().hasNature(Constants.NATURE_ID)) {
return;
}
delta.accept(new IResourceDeltaVisitor() { // NOSONAR - anonymous to
// lambda
@Override
public boolean visit(IResourceDelta delta) throws CoreException {
if (progress.isCanceled()) {
throw new OperationCanceledException();
}
if (delta.getResource() instanceof IProject) {
return delta.getResource() == getProject();
} else if (delta.getResource() instanceof IFolder) {
return checkFolder((IFolder) delta.getResource());
} else if (delta.getResource() instanceof IFile) {
return checkFile((IFile) delta.getResource());
}
return false;
}
});
if (progress.isCanceled())
throw new OperationCanceledException();
progress.worked(4);
}
/**
* @param monitor
* the progress monitor to use for reporting progress to the
* user. It is the caller's responsibility to call done() on the
* given monitor. Accepts null, indicating that no progress
* should be reported and that the operation cannot be cancelled.
*/
protected void fullBuild(final IProgressMonitor monitor) throws CoreException {
IProject project = getProject();
if (!project.getDescription().hasNature(Constants.NATURE_ID)) {
return;
}
// monitor.worked(2);
// project.accept(new IResourceVisitor() {
// @Override
// // NOSONAR - anonymous to lambda
// public boolean visit(IResource resource) throws CoreException {
// if (resource == getProject()) {
// return true;
// } else if (resource instanceof IFolder) {
// return checkFolder((IFolder) resource);
// } else if (resource instanceof IFile) {
// return checkFile((IFile) resource);
// }
// return false;
// }
// });
// monitor.worked(6);
}
/**
* Returns true, if delta is from our projectentity is contained in
* entityModel.
*
* @param entity
* @param entityModel
* @return
*/
protected boolean isOpened(IResourceDelta delta) {
return delta.getResource() instanceof IProject && (delta.getFlags() & IResourceDelta.OPEN) != 0
&& ((IProject) delta.getResource()).isOpen();
}
@Override
protected void clean(IProgressMonitor monitor) throws CoreException {
if (monitor != null) {
monitor.done();
}
}
public static class LocalResourceAccess implements IReferenceFinder.ILocalResourceAccess {
private ResourceSet resourceSet;
public LocalResourceAccess(ResourceSet resourceSet) {
this.resourceSet = resourceSet;
}
public <R> R readOnly(URI targetURI, IUnitOfWork<R, ResourceSet> work) {
try {
return work.exec(resourceSet);
} catch (OperationCanceledException e) {
throw e;
} catch (Exception exc) {
throw new WrappedException(exc);
}
}
}
}