diff options
author | Jan Belle | 2018-10-11 10:15:37 +0000 |
---|---|---|
committer | Jan Belle | 2019-03-08 16:12:05 +0000 |
commit | d47fab84d8d7df4b9d2270d8d153ee57b92f4a83 (patch) | |
tree | 58f0001cf31425426070eca41913be8b62a7ad0f | |
parent | c511bcf1062cc5d8af65be5092a164d62cc017f3 (diff) | |
download | org.eclipse.etrice-d47fab84d8d7df4b9d2270d8d153ee57b92f4a83.tar.gz org.eclipse.etrice-d47fab84d8d7df4b9d2270d8d153ee57b92f4a83.tar.xz org.eclipse.etrice-d47fab84d8d7df4b9d2270d8d153ee57b92f4a83.zip |
[core] Implement uri independent import system
Bug 544947
Change-Id: Ibc5cbbef902247e4b8fc8f7e17b9b5a2b8d95eaa
48 files changed, 1609 insertions, 251 deletions
diff --git a/plugins/org.eclipse.etrice.core.common.ui/META-INF/MANIFEST.MF b/plugins/org.eclipse.etrice.core.common.ui/META-INF/MANIFEST.MF index 629ea41bc..e193bc06a 100644 --- a/plugins/org.eclipse.etrice.core.common.ui/META-INF/MANIFEST.MF +++ b/plugins/org.eclipse.etrice.core.common.ui/META-INF/MANIFEST.MF @@ -4,7 +4,6 @@ Bundle-Name: Common eTrice DSL UI classes Bundle-Vendor: Eclipse eTrice Bundle-Version: 2.0.0.qualifier Bundle-SymbolicName: org.eclipse.etrice.core.common.ui; singleton:=true -Bundle-ActivationPolicy: lazy Require-Bundle: org.eclipse.etrice.core.common;visibility:=reexport, org.eclipse.xtext.ui;bundle-version="2.6.0", org.eclipse.ui.editors;bundle-version="3.5.0", @@ -18,7 +17,8 @@ Require-Bundle: org.eclipse.etrice.core.common;visibility:=reexport, org.eclipse.compare, org.eclipse.core.filesystem;bundle-version="1.3.0", org.eclipse.help, - org.eclipse.xtext.xbase.lib + org.eclipse.xtext.xbase.lib, + org.eclipse.etrice.generator.base Import-Package: org.apache.log4j, org.eclipse.xtext.xbase.lib Bundle-RequiredExecutionEnvironment: JavaSE-1.8 @@ -34,6 +34,8 @@ Export-Package: org.eclipse.etrice.core.common.ui.autoedit, org.eclipse.etrice.core.common.ui.internal, org.eclipse.etrice.core.common.ui.labeling, org.eclipse.etrice.core.common.ui.linking, + org.eclipse.etrice.core.common.ui.modelpath, org.eclipse.etrice.core.common.ui.quickfix -Bundle-Activator: org.eclipse.etrice.core.common.ui.ETriceCoreCommonActivator +Bundle-Activator: org.eclipse.etrice.core.common.ui.internal.BaseActivator Automatic-Module-Name: org.eclipse.etrice.core.common.ui +Bundle-ActivationPolicy: lazy diff --git a/plugins/org.eclipse.etrice.core.common.ui/plugin.xml b/plugins/org.eclipse.etrice.core.common.ui/plugin.xml index f91ef7aac..e4deda339 100644 --- a/plugins/org.eclipse.etrice.core.common.ui/plugin.xml +++ b/plugins/org.eclipse.etrice.core.common.ui/plugin.xml @@ -392,5 +392,11 @@ extensions="etbase"> </provider> </extension> + <extension + point="org.eclipse.xtext.ui.shared.sharedStateContributingModule"> + <module + class="org.eclipse.etrice.core.common.ui.SharedContributionModule"> + </module> + org.eclipse.etrice.core.common.ui.SharedContributionModule </extension> </plugin> diff --git a/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/ETriceCoreCommonActivator.java b/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/ETriceCoreCommonActivator.java deleted file mode 100644 index fb8e463ab..000000000 --- a/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/ETriceCoreCommonActivator.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.eclipse.etrice.core.common.ui; - -import org.eclipse.etrice.core.common.scoping.ModelLocator; -import org.eclipse.etrice.core.common.ui.internal.BaseActivator; -import org.osgi.framework.BundleContext; - -import com.google.inject.Injector; - -public class ETriceCoreCommonActivator extends BaseActivator { - - /* (non-Javadoc) - * @see org.franca.core.dsl.ui.internal.FrancaIDLActivator#start(org.osgi.framework.BundleContext) - */ - @Override - public void start(BundleContext context) throws Exception { - super.start(context); - - Injector injector = getInjector(ORG_ECLIPSE_ETRICE_CORE_COMMON_BASE); - ModelLocator ml = injector.getInstance(ModelLocator.class); - ml.loadExtensions(); - } - -} diff --git a/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/SharedContributionModule.java b/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/SharedContributionModule.java new file mode 100644 index 000000000..508b8c698 --- /dev/null +++ b/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/SharedContributionModule.java @@ -0,0 +1,37 @@ +/******************************************************************************* +* Copyright (c) 2018 protos software gmbh (http://www.protos.de). +* All rights reserved. +* +* 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/ +* +* SPDX-License-Identifier: EPL-2.0 +* +* CONTRIBUTORS: +* Jan Belle (initial contribution) +* + *******************************************************************************/ + +package org.eclipse.etrice.core.common.ui; + +import org.eclipse.etrice.core.common.ui.modelpath.ModelPathChangeListener; +import org.eclipse.etrice.core.common.ui.modelpath.ModelPathResourceSetInitializer; +import org.eclipse.xtext.ui.resource.IResourceSetInitializer; +import org.eclipse.xtext.ui.shared.contribution.IEagerContribution; + +import com.google.inject.Binder; +import com.google.inject.Module; + +/** + * @see org.eclipse.xtext.ui.shared.sharedStateContributingModule + */ +public class SharedContributionModule implements Module { + + @Override + public void configure(Binder binder) { + binder.bind(IEagerContribution.class).to(ModelPathChangeListener.class); + binder.bind(IResourceSetInitializer.class).to(ModelPathResourceSetInitializer.class); + } + +}
\ No newline at end of file diff --git a/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/hover/KeywordHoverProvider.java b/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/hover/KeywordHoverProvider.java index 4df362c36..36f6bcc9f 100644 --- a/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/hover/KeywordHoverProvider.java +++ b/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/hover/KeywordHoverProvider.java @@ -19,7 +19,7 @@ import java.io.IOException; import java.net.URL; import org.eclipse.emf.ecore.EObject; -import org.eclipse.etrice.core.common.ui.ETriceCoreCommonActivator; +import org.eclipse.etrice.core.common.ui.internal.BaseActivator; import org.eclipse.jface.internal.text.html.HTMLPrinter; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.viewers.ILabelProvider; @@ -78,7 +78,7 @@ public class KeywordHoverProvider extends DefaultEObjectHoverProvider { url = plugin.getBundle().getEntry(styleSheetFileName); } else { - url = ETriceCoreCommonActivator.getInstance().getBundle().getEntry("/eTriceKeywordHoverStyle.css"); + url = BaseActivator.getInstance().getBundle().getEntry("/eTriceKeywordHoverStyle.css"); } styleSheet = Files.readStreamIntoString(url.openStream()); } diff --git a/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/linking/ImportAwareHyperlinkHelper.java b/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/linking/ImportAwareHyperlinkHelper.java index 3b0a5d99c..a96071feb 100644 --- a/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/linking/ImportAwareHyperlinkHelper.java +++ b/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/linking/ImportAwareHyperlinkHelper.java @@ -17,21 +17,30 @@ package org.eclipse.etrice.core.common.ui.linking; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EcoreFactory; +import org.eclipse.emf.ecore.EcorePackage; import org.eclipse.etrice.core.common.base.Import; import org.eclipse.etrice.core.common.scoping.ModelLocatorUriResolver; import org.eclipse.jface.text.Region; import org.eclipse.xtext.AbstractRule; import org.eclipse.xtext.RuleCall; +import org.eclipse.xtext.naming.IQualifiedNameConverter; +import org.eclipse.xtext.naming.QualifiedName; import org.eclipse.xtext.nodemodel.ILeafNode; import org.eclipse.xtext.nodemodel.INode; import org.eclipse.xtext.nodemodel.util.NodeModelUtils; import org.eclipse.xtext.parser.IParseResult; import org.eclipse.xtext.resource.EObjectAtOffsetHelper; +import org.eclipse.xtext.resource.IEObjectDescription; import org.eclipse.xtext.resource.XtextResource; +import org.eclipse.xtext.scoping.IGlobalScopeProvider; +import org.eclipse.xtext.scoping.IScope; import org.eclipse.xtext.ui.editor.hyperlinking.HyperlinkHelper; import org.eclipse.xtext.ui.editor.hyperlinking.IHyperlinkAcceptor; import org.eclipse.xtext.ui.editor.hyperlinking.XtextHyperlink; +import com.google.common.base.Predicates; import com.google.inject.Inject; import com.google.inject.Provider; @@ -49,6 +58,12 @@ public class ImportAwareHyperlinkHelper extends HyperlinkHelper { @Inject protected EObjectAtOffsetHelper eObjectAtOffsetHelper; + + @Inject + protected IGlobalScopeProvider globalScopeProvider; + + @Inject + protected IQualifiedNameConverter nameConverter; /* * (non-Javadoc) @@ -74,6 +89,29 @@ public class ImportAwareHyperlinkHelper extends HyperlinkHelper { return null; Import importObj = (Import) eObject; + + // Create hyperlink based on the qualified name of the import using the global scope provider + String name = importObj.getImportedNamespace(); + if(name != null) { + QualifiedName qualifiedName = nameConverter.toQualifiedName(name); + if(!qualifiedName.getLastSegment().equals("*")) { + EReference reference = EcoreFactory.eINSTANCE.createEReference(); + reference.setEType(EcorePackage.eINSTANCE.getEObject()); + IScope scope = globalScopeProvider.getScope(resource, reference, Predicates.alwaysTrue()); + IEObjectDescription eod = scope.getSingleElement(qualifiedName); + if(eod != null) { + URI uri = eod.getEObjectURI(); + INode node = NodeModelUtils.getNode(importObj); + XtextHyperlink result = hyperlinkProvider.get(); + result.setHyperlinkText(eod.getName().toString()); + result.setURI(uri); + result.setHyperlinkRegion(new Region(node.getOffset(), node.getLength())); // whole import statement + return result; + } + } + } + + // Create hyperlink using the import uri if (importObj.getImportURI() == null) return null; diff --git a/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/modelpath/ModelPathChangeListener.java b/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/modelpath/ModelPathChangeListener.java new file mode 100644 index 000000000..5cc5a1d66 --- /dev/null +++ b/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/modelpath/ModelPathChangeListener.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2019 protos software gmbh (http://www.protos.de). + * 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: + * Jan Belle (initial contribution) + * + *******************************************************************************/ + +package org.eclipse.etrice.core.common.ui.modelpath; + +import java.util.Collection; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.etrice.core.common.ui.modelpath.ModelPathManager.IModelPathListener; +import org.eclipse.xtext.builder.impl.BuildScheduler; +import org.eclipse.xtext.builder.impl.IBuildFlag; +import org.eclipse.xtext.resource.impl.CoarseGrainedChangeEvent; +import org.eclipse.xtext.ui.editor.IDirtyStateManager; +import org.eclipse.xtext.ui.shared.contribution.IEagerContribution; + +import com.google.inject.Inject; + +/** + * Listens for modelpath changes and triggers a rebuild if necessary. + */ +@SuppressWarnings("restriction") +public class ModelPathChangeListener implements IEagerContribution, IModelPathListener { + + private BuildScheduler buildScheduler; + private IDirtyStateManager dirtyStateManager; + + @Inject + public ModelPathChangeListener(BuildScheduler buildScheduler, IDirtyStateManager dirtyStateManager) { + this.buildScheduler = buildScheduler; + this.dirtyStateManager = dirtyStateManager; + } + + @Override + public void onChange(Collection<IProject> projects) { + // Notify xtext of modelpath changes + dirtyStateManager.notifyListeners(new CoarseGrainedChangeEvent()); + + // Trigger build for all projects if auto build is enabled + if(ResourcesPlugin.getWorkspace().isAutoBuilding()) { + buildScheduler.scheduleBuildIfNecessary(projects, IBuildFlag.FORGET_BUILD_STATE_ONLY); + } + } + + @Override + public void initialize() { + ModelPathManager.INSTANCE.addListener(this); + ModelPathManager.INSTANCE.startListening(); + } + + @Override + public void discard() { + ModelPathManager.INSTANCE.stopListening(); + ModelPathManager.INSTANCE.removeListener(this); + } + +} diff --git a/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/modelpath/ModelPathDescription.java b/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/modelpath/ModelPathDescription.java new file mode 100644 index 000000000..380365055 --- /dev/null +++ b/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/modelpath/ModelPathDescription.java @@ -0,0 +1,171 @@ +/******************************************************************************* + * Copyright (c) 2019 protos software gmbh (http://www.protos.de). + * 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: + * Jan Belle (initial contribution) + * + *******************************************************************************/ + +package org.eclipse.etrice.core.common.ui.modelpath; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.UncheckedIOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.Path; + +/** + * Represent a modelpath description. + */ +public class ModelPathDescription { + + private final List<IFolder> sourceDirectories; + private final List<IProject> projectDependencies; + + private ModelPathDescription(List<IFolder> sourceDirectories, List<IProject> projectDependencies) { + this.sourceDirectories = sourceDirectories; + this.projectDependencies = projectDependencies; + } + + /** + * @return the list of source directories + */ + public List<IFolder> getSourceDirectories() { + return sourceDirectories; + } + + /** + * @return the list of project dependencies + */ + public List<IProject> getProjectDependencies() { + return projectDependencies; + } + + /** + * Loads the modelpath description from a file. + * + * @param file the file to load from + * @return the loaded modelpath description + * + * @throws CoreException if the file doesn't exist or is out of sync + * @throws UncheckedIOException if an io exception occurs + */ + public static ModelPathDescription load(IFile file) throws CoreException { + try { + return new ModelPathDescriptionLoader(file).load(); + } + catch(IOException e) { + throw new UncheckedIOException(e); + } + } + + /** + * Helper class to parse and validate modelpath description files. + */ + private static class ModelPathDescriptionLoader { + + private static final String KEYWORD_SRCDIR = "srcDir"; + private static final String KEYWORD_PROJECT = "project"; + private static final Pattern KEYWORD_PATTERN = Pattern.compile("\\S+"); + + private IFile file; + private IProject project; + private IWorkspaceRoot root; + private List<IFolder> srcDirs; + private List<IProject> projects; + private int lineNumber; + + public ModelPathDescriptionLoader(IFile file) { + this.file = file; + project = file.getProject(); + root = ResourcesPlugin.getWorkspace().getRoot(); + srcDirs = new ArrayList<>(); + projects = new ArrayList<>(); + lineNumber = 0; + } + + public ModelPathDescription load() throws CoreException, IOException { + file.deleteMarkers(IMarker.PROBLEM, false, IResource.DEPTH_ZERO); + + try(BufferedReader reader = new BufferedReader(new InputStreamReader(file.getContents()))) { + for(String line = reader.readLine(); line != null; line = reader.readLine(), lineNumber++) { + parseLine(line); + } + } + + return new ModelPathDescription(Collections.unmodifiableList(srcDirs), Collections.unmodifiableList(projects)); + } + + private void parseLine(String line) throws CoreException { + Matcher matcher = KEYWORD_PATTERN.matcher(line); + if(matcher.find()) { + String keyword = matcher.group(); + String str = line.substring(matcher.end()).trim(); + + switch(keyword) { + case KEYWORD_SRCDIR: + parseSrcDir(str); + break; + case KEYWORD_PROJECT: + parseProject(str); + break; + default: + createProblemMarker(IMarker.SEVERITY_ERROR, "unexpected keyword " + keyword); + } + } + } + + private void parseSrcDir(String str) throws CoreException { + if(Path.EMPTY.isValidPath(str)) { + IFolder dir = project.getFolder(str); + if(!dir.exists()) { + createProblemMarker(IMarker.SEVERITY_WARNING, "directory " + dir.getFullPath() + " doesn't exist"); + } + srcDirs.add(dir); + } + else { + createProblemMarker(IMarker.SEVERITY_ERROR, str + " isn't a valid path"); + } + } + + private void parseProject(String str) throws CoreException { + if(Path.EMPTY.isValidSegment(str)) { + IProject project = root.getProject(str); + if(!project.isAccessible()) { + createProblemMarker(IMarker.SEVERITY_WARNING, "project " + project.getName() + " doesn't exist"); + } + projects.add(project); + } + else { + createProblemMarker(IMarker.SEVERITY_ERROR, str + " isn't a valid project name"); + } + } + + private void createProblemMarker(int severity, String message) throws CoreException { + IMarker marker = file.createMarker(IMarker.PROBLEM); + marker.setAttribute(IMarker.SEVERITY, severity); + marker.setAttribute(IMarker.LINE_NUMBER, lineNumber + 1); + marker.setAttribute(IMarker.MESSAGE, message); + } + } +}
\ No newline at end of file diff --git a/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/modelpath/ModelPathManager.java b/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/modelpath/ModelPathManager.java new file mode 100644 index 000000000..590bd32b0 --- /dev/null +++ b/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/modelpath/ModelPathManager.java @@ -0,0 +1,246 @@ +/******************************************************************************* + * Copyright (c) 2019 protos software gmbh (http://www.protos.de). + * 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: + * Jan Belle (initial contribution) + * + *******************************************************************************/ + +package org.eclipse.etrice.core.common.ui.modelpath; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import org.eclipse.core.resources.IContainer; +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.IResourceChangeEvent; +import org.eclipse.core.resources.IResourceChangeListener; +import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.resources.WorkspaceJob; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; + +/** + * Maintains the modelpaths for all eclipse projects. + */ +public class ModelPathManager implements IResourceChangeListener { + + public static final ModelPathManager INSTANCE = new ModelPathManager(); + + public static final Path MODELPATH_FILE = new Path(".modelpath"); + + private HashMap<IProject, ModelPathDescription> descriptionCache; + private HashMap<IProject, WorkspaceModelPath> modelpathCache; + private List<IModelPathListener> listeners; + + @FunctionalInterface + public interface IModelPathListener { + + /** + * Notifies of a change in the modelpath description file. + * + * @param projects whose modelpath description has been changed + */ + void onChange(Collection<IProject> projects); + } + + private ModelPathManager() { + descriptionCache = new HashMap<>(); + modelpathCache = new HashMap<>(); + listeners = new ArrayList<>(); + } + + /** + * Retrieves the modelpath for the specified project. + * + * @param project a project of the workspace + * @return the modelpath of project + */ + public synchronized WorkspaceModelPath getModelPath(IProject project) { + return modelpathCache.computeIfAbsent(project, this::computeModelPath); + } + + /** + * Adds a modelpath listener. + * + * @param listener the listener to add + */ + public synchronized void addListener(IModelPathListener listener) { + listeners.add(listener); + } + + /** + * Removes a modelpath listener. + * + * @param listener the listener to remove + */ + public synchronized void removeListener(IModelPathListener listener) { + listeners.remove(listener); + } + + /** + * Starts listening for changes in modelpath description files. + */ + public void startListening() { + ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE); + } + + /** + * Stops listening for changes in modelpath description files. + */ + public void stopListening() { + ResourcesPlugin.getWorkspace().removeResourceChangeListener(this); + } + + @Override + public void resourceChanged(IResourceChangeEvent event) { + // Check for changes in the modelpath description files + IResourceDelta rootDelta = event.getDelta(); + IResourceDelta[] projectDeltas = rootDelta.getAffectedChildren(); + ArrayList<IProject> projects = new ArrayList<>(); + for(IResourceDelta projectDelta: projectDeltas) { + IResourceDelta modelpathDelta = projectDelta.findMember(MODELPATH_FILE); + if(modelpathDelta != null && + ((modelpathDelta.getFlags() & IResourceDelta.CONTENT) != 0 + || modelpathDelta.getKind() == IResourceDelta.ADDED + || modelpathDelta.getKind() == IResourceDelta.REMOVED)) { + IProject project = (IProject) projectDelta.getResource(); + projects.add(project); + } + } + + // Schedule update of modelpath if changes were detected + if(!projects.isEmpty()) { + new ModelPathUpdateJob(projects).schedule(); + } + } + + /** + * Computes a workspace modelpath for the specified project. + * + * @param project a project + * @return the workspace modelpath for the project + */ + private WorkspaceModelPath computeModelPath(IProject project) { + Set<IProject> dependencies = new HashSet<>(); + addAllProjectDependencies(project, dependencies); + List<IContainer> paths = dependencies.stream() + .flatMap(p -> getSourceDirectories(p).stream()) + .collect(Collectors.toList()); + return new WorkspaceModelPath(paths); + } + + /** + * Recursively adds all (including transitive) project dependencies of a project to the set of dependencies. + * + * @param project a project + * @param dependencies a set to add the project dependencies + */ + private void addAllProjectDependencies(IProject project, Set<IProject> dependencies) { + if(dependencies.add(project)) { + getProjectDependencies(project).forEach(p -> addAllProjectDependencies(p, dependencies)); + } + } + + /** + * Returns the modelpath description project dependencies of a project. + * + * @param project a project + * @return a list of projects + */ + private List<IProject> getProjectDependencies(IProject project) { + return getModelPathDescription(project) + .map(ModelPathDescription::getProjectDependencies) + .orElse(Collections.emptyList()); + } + + /** + * Return the modelpath description source directories of a project. + * + * @param project a project + * @return a list of source directories + */ + private List<IFolder> getSourceDirectories(IProject project) { + return getModelPathDescription(project) + .map(ModelPathDescription::getSourceDirectories) + .orElse(Collections.emptyList()); + } + + /** + * Retrieves a modelpath description for a project from the cache. + * If the description is not in the cache yet, it is loaded and added to the cache. + * + * @param project a project + * @return the modelpath description of the project or nothing if the modelpath description file does not exist + */ + private Optional<ModelPathDescription> getModelPathDescription(IProject project) { + return Optional.ofNullable(descriptionCache.computeIfAbsent(project, this::loadModelPathDescription)); + } + + /** + * Loads the modelpath description file of a project. + * + * @param project a project + * @return the modelpath description or null if the file does not exist + */ + private ModelPathDescription loadModelPathDescription(IProject project) { + IFile file = project.getFile(MODELPATH_FILE); + if(file.exists()) { + try { + return ModelPathDescription.load(file); + } + catch(CoreException e) { + try { + file.refreshLocal(IResource.DEPTH_ZERO, null); + } + catch(CoreException ex) {} + } + } + return null; + } + + private class ModelPathUpdateJob extends WorkspaceJob { + + private Collection<IProject> projects; + + public ModelPathUpdateJob(Collection<IProject> projects) { + super("modelpath updater"); + this.projects = projects; + } + + @Override + public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException { + synchronized(ModelPathManager.this) { + // Reload changed modelpath description files + projects.forEach(project -> descriptionCache.compute(project, (p, d) -> loadModelPathDescription(p))); + + // Clear modelpath cache + modelpathCache.clear(); + + // Notify listeners of change + listeners.forEach(listener -> listener.onChange(projects)); + } + + return Status.OK_STATUS; + } + } +} diff --git a/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/modelpath/ModelPathResourceSetInitializer.java b/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/modelpath/ModelPathResourceSetInitializer.java new file mode 100644 index 000000000..3d7c87124 --- /dev/null +++ b/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/modelpath/ModelPathResourceSetInitializer.java @@ -0,0 +1,57 @@ +/******************************************************************************* +* Copyright (c) 2018 protos software gmbh (http://www.protos.de). +* All rights reserved. +* +* 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/ +* +* SPDX-License-Identifier: EPL-2.0 +* +* CONTRIBUTORS: +* Jan Belle (initial contribution) +* + *******************************************************************************/ + +package org.eclipse.etrice.core.common.ui.modelpath; + +import java.util.stream.Stream; + +import org.eclipse.core.resources.IProject; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.etrice.generator.base.io.IModelPath; +import org.eclipse.etrice.generator.base.io.ResourceSetModelPathProvider; +import org.eclipse.xtext.naming.QualifiedName; +import org.eclipse.xtext.ui.resource.IResourceSetInitializer; + +/** + * Adds the modelpath to the resource set load options. + */ +public class ModelPathResourceSetInitializer implements IResourceSetInitializer { + + @Override + public void initialize(ResourceSet resourceSet, IProject project) { + ModelPathDelegate modelPath = new ModelPathDelegate(project); + resourceSet.getLoadOptions().put(ResourceSetModelPathProvider.LOAD_OPTION_MODELPATH, modelPath); + } + + private class ModelPathDelegate implements IModelPath { + + private IProject project; + + public ModelPathDelegate(IProject project) { + this.project = project; + } + + @Override + public Stream<URI> getFiles(QualifiedName name) { + return ModelPathManager.INSTANCE.getModelPath(project).getFiles(name); + } + + @Override + public Stream<URI> getAllFiles() { + return ModelPathManager.INSTANCE.getModelPath(project).getAllFiles(); + } + } +} diff --git a/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/modelpath/WorkspaceModelPath.java b/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/modelpath/WorkspaceModelPath.java new file mode 100644 index 000000000..5daf25a4d --- /dev/null +++ b/plugins/org.eclipse.etrice.core.common.ui/src/org/eclipse/etrice/core/common/ui/modelpath/WorkspaceModelPath.java @@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright (c) 2019 protos software gmbh (http://www.protos.de). + * 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: + * Jan Belle (initial contribution) + * + *******************************************************************************/ + +package org.eclipse.etrice.core.common.ui.modelpath; + +import java.util.List; +import java.util.stream.Stream; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.Path; +import org.eclipse.emf.common.util.URI; +import org.eclipse.etrice.generator.base.io.IModelPath; +import org.eclipse.xtext.naming.QualifiedName; + +/** + * Modelpath implementation based on the eclipse workspace. + */ +public class WorkspaceModelPath implements IModelPath { + + private List<IContainer> paths; + + /** + * Constructs a new modelpath. + * + * @param paths the paths to seach for model files + */ + public WorkspaceModelPath(List<IContainer> paths) { + this.paths = paths; + } + + @Override + public Stream<URI> getFiles(QualifiedName name) { + Stream<URI> stream = Stream.empty(); + while(!name.isEmpty()) { + name = name.skipLast(1); + stream = Stream.concat(stream, getPackage(name)); + } + return stream; + } + + @Override + public Stream<URI> getAllFiles() { + return paths.stream() + .filter(container -> container.isAccessible()) + .flatMap(container -> getAllFiles(container)); + } + + public List<IContainer> getPaths() { + return paths; + } + + @Override + public String toString() { + return paths.toString(); + } + + /** + * Constructrs a stream of file contained in the specified package. + * + * @param name the fully qualified name of the package + * @return a stream of file uris + */ + private Stream<URI> getPackage(QualifiedName name) { + String pathStr = name.toString().replace('.', '/'); + Path path = new Path(pathStr); + return paths.stream() + .map(container -> container.getFolder(path)) + .filter(folder -> folder.exists()) + .flatMap(folder -> getFiles(folder)); + } + + /** + * Constructs a stream of files contained in the specified container. + * + * @param container the container to be searched for files + * @return a stream of file uris + */ + private Stream<URI> getFiles(IContainer container) { + return getMembers(container) + .filter(resource -> resource.getType() == IResource.FILE) + .map(IFile.class::cast) + .map(file -> createURI(file)); + } + + /** + * Constructs a stream that contains all files in the container including files of subfolders. + * + * @param container the container to be searched for files + * @return a stream of file uris + */ + private Stream<URI> getAllFiles(IContainer container) { + return getMembers(container) + .flatMap(resource -> { + if(resource.getType() == IResource.FILE) { + IFile file = (IFile) resource; + URI uri = createURI(file); + return Stream.of(uri); + } + else if(resource.getType() == IResource.FOLDER) { + IFolder subfolder = (IFolder) resource; + return getAllFiles(subfolder); + } + return Stream.empty(); + }); + } + + /** + * Creates an uri for the passed file. + */ + private URI createURI(IFile file) { + return URI.createPlatformResourceURI(file.getFullPath().toString(), true); + } + + /** + * Returns a stream of resources contained in the passed container. + */ + private Stream<IResource> getMembers(IContainer container) { + try { + return Stream.of(container.members()); + } + catch(CoreException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/plugins/org.eclipse.etrice.core.common/META-INF/MANIFEST.MF b/plugins/org.eclipse.etrice.core.common/META-INF/MANIFEST.MF index 9f854a089..fa63d005d 100644 --- a/plugins/org.eclipse.etrice.core.common/META-INF/MANIFEST.MF +++ b/plugins/org.eclipse.etrice.core.common/META-INF/MANIFEST.MF @@ -20,7 +20,8 @@ Require-Bundle: org.eclipse.xtext;bundle-version="2.6.0";visibility:=reexport, org.antlr.runtime, org.eclipse.core.runtime, org.eclipse.core.resources, - org.eclipse.xtext.xbase.lib + org.eclipse.xtext.xbase.lib, + org.eclipse.etrice.generator.base Import-Package: org.apache.log4j, org.eclipse.xtext.xbase.lib Bundle-RequiredExecutionEnvironment: JavaSE-1.8 diff --git a/plugins/org.eclipse.etrice.core.common/build.gradle b/plugins/org.eclipse.etrice.core.common/build.gradle index 38c3c557b..628c56fb7 100644 --- a/plugins/org.eclipse.etrice.core.common/build.gradle +++ b/plugins/org.eclipse.etrice.core.common/build.gradle @@ -4,4 +4,6 @@ dependencies { compile "org.eclipse.platform:org.eclipse.core.resources:$versions.core_resources" compile "org.eclipse.xtext:org.eclipse.xtext:$versions.xtext" compile "com.google.guava:guava:$versions.guava" + + compile project(':plugins:org.eclipse.etrice.generator.base') }
\ No newline at end of file diff --git a/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/scoping/CompoundGlobalScopeProvider.java b/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/scoping/CompoundGlobalScopeProvider.java new file mode 100644 index 000000000..b24ebe198 --- /dev/null +++ b/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/scoping/CompoundGlobalScopeProvider.java @@ -0,0 +1,46 @@ +/******************************************************************************* +* Copyright (c) 2018 protos software gmbh (http://www.protos.de). +* All rights reserved. +* +* 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/ +* +* SPDX-License-Identifier: EPL-2.0 +* +* CONTRIBUTORS: +* Jan Belle (initial contribution) +* + *******************************************************************************/ + +package org.eclipse.etrice.core.common.scoping; + +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.xtext.resource.IEObjectDescription; +import org.eclipse.xtext.scoping.IGlobalScopeProvider; +import org.eclipse.xtext.scoping.IScope; +import org.eclipse.xtext.scoping.impl.ImportUriGlobalScopeProvider; + +import com.google.common.base.Predicate; +import com.google.inject.Inject; + +/** + * Global scope provider that combines the {@link ImportUriGlobalScopeProvider} and {@link ModelPathGlobalScopeProvider}. + */ +public class CompoundGlobalScopeProvider implements IGlobalScopeProvider { + + @Inject + ModelPathGlobalScopeProvider modelPathGlobalScopeProvider; + + @Inject + ImportUriGlobalScopeProvider importURIGlobalSopeProvider; + + @Override + public IScope getScope(Resource context, EReference reference, Predicate<IEObjectDescription> filter) { + IScope uriScope = importURIGlobalSopeProvider.getScope(context, reference, filter); + IScope combinedScope = modelPathGlobalScopeProvider.getScope(uriScope, context, reference, filter); + + return combinedScope; + } +} diff --git a/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/scoping/ModelLocator.java b/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/scoping/ModelLocator.java index 706d92587..ea185c076 100644 --- a/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/scoping/ModelLocator.java +++ b/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/scoping/ModelLocator.java @@ -16,12 +16,14 @@ package org.eclipse.etrice.core.common.scoping; import java.util.ArrayList; -import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.Platform; +import org.eclipse.emf.common.EMFPlugin; import org.eclipse.emf.ecore.resource.Resource; +import org.osgi.framework.Bundle; import com.google.inject.Inject; +import com.google.inject.Injector; import com.google.inject.Singleton; /** @@ -39,20 +41,27 @@ public class ModelLocator { addLocator(defaultLocator); } - public void loadExtensions() { - IConfigurationElement[] config = Platform.getExtensionRegistry() - .getConfigurationElementsFor(IMODEL_LOCATOR_ID); - - for (IConfigurationElement e : config) { - try { - final Object ext = e.createExecutableExtension("class"); - if (ext instanceof IModelLocator) { - IModelLocator locator = (IModelLocator) ext; - locators.add(locator); + @Inject + public void loadExtensions(Injector injector) { + if(EMFPlugin.IS_ECLIPSE_RUNNING && Platform.getExtensionRegistry() != null) { + IConfigurationElement[] config = Platform.getExtensionRegistry() + .getConfigurationElementsFor(IMODEL_LOCATOR_ID); + + for (IConfigurationElement e : config) { + try { + final String extContributor = e.getContributor().getName(); + final Bundle extBundle = Platform.getBundle(extContributor); + final String extClassName = e.getAttribute("class"); + final Class<?> extClass = extBundle.loadClass(extClassName); + final Object ext = injector.getInstance(extClass); + if (ext instanceof IModelLocator) { + IModelLocator locator = (IModelLocator) ext; + locators.add(locator); + } + } + catch (ClassNotFoundException ex) { + System.out.println(ex.getMessage()); } - } - catch (CoreException ex) { - System.out.println(ex.getMessage()); } } } diff --git a/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/scoping/ModelPathFileExtensionFilter.java b/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/scoping/ModelPathFileExtensionFilter.java new file mode 100644 index 000000000..b6899729d --- /dev/null +++ b/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/scoping/ModelPathFileExtensionFilter.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2019 protos software gmbh (http://www.protos.de). + * 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: + * Jan Belle (initial contribution) + * + *******************************************************************************/ + +package org.eclipse.etrice.core.common.scoping; + +import java.util.Arrays; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.etrice.core.common.scoping.ModelPathGlobalScopeProvider.IModelPathFileFilter; + +/** + * Filters the model path by file extension. + */ +public class ModelPathFileExtensionFilter implements IModelPathFileFilter { + + private final String[] legalFileExtensions; + + /** + * Constructs new model path filter that only allows files with specific file extenions. + * + * @param legalFileExtensions an array of permitted file extensions + */ + public ModelPathFileExtensionFilter(String... legalFileExtensions) { + this.legalFileExtensions = legalFileExtensions; + Arrays.sort(this.legalFileExtensions); + } + + @Override + public boolean apply(URI uri) { + String fileExtension = uri.fileExtension(); + if(fileExtension != null) { + return Arrays.binarySearch(legalFileExtensions, fileExtension) >= 0; + } + return false; + } + +} diff --git a/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/scoping/ModelPathGlobalScopeProvider.java b/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/scoping/ModelPathGlobalScopeProvider.java new file mode 100644 index 000000000..c2555c368 --- /dev/null +++ b/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/scoping/ModelPathGlobalScopeProvider.java @@ -0,0 +1,215 @@ +/******************************************************************************* +* Copyright (c) 2018 protos software gmbh (http://www.protos.de). +* All rights reserved. +* +* 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/ +* +* SPDX-License-Identifier: EPL-2.0 +* +* CONTRIBUTORS: +* Jan Belle (initial contribution) +* + *******************************************************************************/ + +package org.eclipse.etrice.core.common.scoping; + +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.etrice.generator.base.io.IModelPath; +import org.eclipse.etrice.generator.base.io.IModelPathProvider; +import org.eclipse.xtext.naming.IQualifiedNameProvider; +import org.eclipse.xtext.naming.QualifiedName; +import org.eclipse.xtext.resource.IEObjectDescription; +import org.eclipse.xtext.resource.IResourceDescription; +import org.eclipse.xtext.resource.IResourceDescriptions; +import org.eclipse.xtext.resource.IResourceServiceProvider; +import org.eclipse.xtext.scoping.IScope; +import org.eclipse.xtext.scoping.impl.AbstractGlobalScopeProvider; +import org.eclipse.xtext.scoping.impl.AbstractScope; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.inject.Inject; + +/** + * Provides scopes based on a {@link IModelPath}. + */ +public class ModelPathGlobalScopeProvider extends AbstractGlobalScopeProvider { + + /** + * Filters the model files. + */ + @FunctionalInterface + public static interface IModelPathFileFilter extends Predicate<URI> {} + + private IModelPathProvider modelPathProvider; + private IQualifiedNameProvider qualifiedNameProvider; + private IResourceServiceProvider.Registry resourceServiceProviderRegistry; + private IModelPathFileFilter modelPathFileFilter; + private ModelLocatorUriResolver uriResolver; + + @Inject + public ModelPathGlobalScopeProvider(IModelPathProvider modelPathProvider, IQualifiedNameProvider qualifiedNameProvider, + IResourceServiceProvider.Registry resourceServiceProviderRegistry, IModelPathFileFilter modelPathFileFilter, ModelLocatorUriResolver uriResolver) { + this.modelPathProvider = modelPathProvider; + this.qualifiedNameProvider = qualifiedNameProvider; + this.resourceServiceProviderRegistry = resourceServiceProviderRegistry; + this.modelPathFileFilter = modelPathFileFilter; + this.uriResolver = uriResolver; + } + + @Override + protected IScope getScope(Resource context, boolean ignoreCase, EClass type, Predicate<IEObjectDescription> filter) { + return new ModelPathGlobalScope(ignoreCase, context, type, filter); + } + + public AbstractScope getScope(IScope parent, Resource context, EReference reference, Predicate<IEObjectDescription> filter) { + return new ModelPathGlobalScope(parent, isIgnoreCase(reference), context, reference.getEReferenceType(), filter); + } + + private class ModelPathGlobalScope extends AbstractScope { + + private Resource context; + private EClass type; + private Predicate<IEObjectDescription> filter; + + public ModelPathGlobalScope(boolean ignoreCase, Resource context, EClass type, Predicate<IEObjectDescription> filter) { + this(IScope.NULLSCOPE, ignoreCase, context, type, filter); + } + + public ModelPathGlobalScope(IScope parent, boolean ignoreCase, + Resource context, EClass type, Predicate<IEObjectDescription> filter) { + super(parent, ignoreCase); + + this.context = context; + this.type = type; + this.filter = filter == null ? Predicates.alwaysTrue() : filter; + } + + @Override + protected IEObjectDescription getSingleLocalElementByName(QualifiedName name) { + Stream<IResourceDescription> resourceDescriptions = getResourceDescriptionsByName(name); + return getExportedObjectsByName(resourceDescriptions, name).findFirst().orElse(null); + } + + @Override + protected Iterable<IEObjectDescription> getLocalElementsByName(final QualifiedName name) { + Stream<IResourceDescription> resourceDescriptions = getResourceDescriptionsByName(name); + return getExportedObjectsByName(resourceDescriptions, name).collect(Collectors.toList()); + } + +// @Override +// protected IEObjectDescription getSingleLocalElementByEObject(final EObject object, final URI uri) { +// Stream<IResourceDescription> resourceDescriptions = getResourceDescriptionsByObject(object); +// return getExportedObjectsByObject(resourceDescriptions, object).findFirst().orElse(null); +// } + + @Override + protected Iterable<IEObjectDescription> getLocalElementsByEObject(final EObject object, final URI uri) { + Stream<IResourceDescription> resourceDescriptions = getResourceDescriptionsByObject(object); + return getExportedObjectsByObject(resourceDescriptions, object).collect(Collectors.toList()); + } + + @Override + protected Iterable<IEObjectDescription> getAllLocalElements() { + Stream<IResourceDescription> resourceDescriptions = getAllResourceDescriptions(); + return getExportedObjectsByType(resourceDescriptions, type).collect(Collectors.toList()); + } + + private Stream<IEObjectDescription> getExportedObjectsByName(Stream<IResourceDescription> resourceDescriptions, QualifiedName name) { + return resourceDescriptions.map(rd -> rd.getExportedObjects(type, name, isIgnoreCase())) + .flatMap(eods -> StreamSupport.stream(eods.spliterator(), false)) + .filter(filter::apply); + } + + private Stream<IEObjectDescription> getExportedObjectsByType(Stream<IResourceDescription> resourceDescriptions, EClass type) { + return resourceDescriptions.map(rd -> rd.getExportedObjectsByType(type)) + .flatMap(eods -> StreamSupport.stream(eods.spliterator(), false)) + .filter(filter::apply); + } + + private Stream<IEObjectDescription> getExportedObjectsByObject(Stream<IResourceDescription> resourceDescriptions, EObject object) { + return resourceDescriptions.map(rd -> rd.getExportedObjectsByObject(object)) + .flatMap(eods -> StreamSupport.stream(eods.spliterator(), false)) + .filter(filter::apply); + } + + private Stream<IResourceDescription> getResourceDescriptionsByObject(EObject object) { + QualifiedName name = qualifiedNameProvider.getFullyQualifiedName(object); + if(name != null) { + return getResourceDescriptionsByName(name); + } + return Stream.empty(); + } + + private Stream<IResourceDescription> getResourceDescriptionsByName(QualifiedName name) { + IModelPath modelPath = modelPathProvider.get(context); + Stream<URI> files = modelPath.getFiles(name); + return getResourceDescriptions(files); + } + + private Stream<IResourceDescription> getAllResourceDescriptions() { + IModelPath modelPath = modelPathProvider.get(context); + Stream<URI> files = modelPath.getAllFiles(); + return getResourceDescriptions(files); + } + + private Stream<IResourceDescription> getResourceDescriptions(Stream<URI> files) { + IResourceDescriptions descriptions = ModelPathGlobalScopeProvider.this.getResourceDescriptions(context); + ResourceSet resourceSet = context.getResourceSet(); + return files.filter(modelPathFileFilter) + .map(uri -> getResourceDescription(uri, descriptions, resourceSet)) + .flatMap(rd -> streamOptional(rd)); + } + + /** + * Tries to retrieve a resource description for the specified resource. + * If the resource description is not present in the index, a new resource description is computed using the resource set. + * + * @param uri the uri of the resource + * @param descriptions the index + * @param resourceSet a resource set used to load the resource + * @return an optional of the requested resource descriptions + */ + private Optional<IResourceDescription> getResourceDescription(URI uri, IResourceDescriptions descriptions, ResourceSet resourceSet) { + uri = resolveURI(uri); + IResourceDescription resourceDescription = descriptions.getResourceDescription(uri); + if(resourceDescription != null) { + return Optional.of(resourceDescription); + } + else { + IResourceServiceProvider resourceServiceProvider = resourceServiceProviderRegistry.getResourceServiceProvider(uri); + if(resourceServiceProvider != null) { + Resource resource = resourceSet.getResource(uri, true); + IResourceDescription.Manager descriptionManager = resourceServiceProvider.getResourceDescriptionManager(); + resourceDescription = descriptionManager.getResourceDescription(resource); + return Optional.of(resourceDescription); + } + } + return Optional.empty(); + } + + private URI resolveURI(URI uri) { + String uriStr = uriResolver.resolve(uri.toString(), null); + return URI.createURI(uriStr); + } + } + + /** + * This method for {@link Optional} is first introduced in Java 9 :( + */ + private static <T> Stream<T> streamOptional(Optional<T> optional) { + return optional.isPresent() ? Stream.of(optional.get()) : Stream.empty(); + } + +} diff --git a/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/scoping/StandardModelLocator.java b/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/scoping/StandardModelLocator.java index b19645b49..0f1c81acc 100644 --- a/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/scoping/StandardModelLocator.java +++ b/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/scoping/StandardModelLocator.java @@ -85,7 +85,8 @@ public class StandardModelLocator implements IModelLocator { private String resolve(String resolve, URI baseUri, Resource res) { resolve = substituteEnvVars(resolve); - // replace (double) slashes and backslashes with single slashes + // replace (double/triple) slashes and backslashes with single slashes + resolve = resolve.replaceAll("///", "/"); resolve = resolve.replaceAll("\\\\", "/"); resolve = resolve.replaceAll("//", "/"); diff --git a/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/validation/BaseJavaValidator.java b/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/validation/BaseJavaValidator.java index d8601c8c8..2e29cf4a7 100644 --- a/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/validation/BaseJavaValidator.java +++ b/plugins/org.eclipse.etrice.core.common/src/org/eclipse/etrice/core/common/validation/BaseJavaValidator.java @@ -18,6 +18,9 @@ import java.util.HashSet; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EcoreFactory; +import org.eclipse.emf.ecore.EcorePackage; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.eclipse.etrice.core.common.base.Annotation; @@ -33,9 +36,15 @@ import org.eclipse.etrice.core.common.base.KeyValue; import org.eclipse.etrice.core.common.base.RealLiteral; import org.eclipse.etrice.core.common.base.SimpleAnnotationAttribute; import org.eclipse.etrice.core.common.base.StringLiteral; +import org.eclipse.xtext.naming.IQualifiedNameConverter; +import org.eclipse.xtext.naming.QualifiedName; +import org.eclipse.xtext.resource.IEObjectDescription; +import org.eclipse.xtext.scoping.IGlobalScopeProvider; +import org.eclipse.xtext.scoping.IScope; import org.eclipse.xtext.scoping.impl.ImportUriResolver; import org.eclipse.xtext.validation.Check; +import com.google.common.base.Predicates; import com.google.inject.Inject; /** @@ -52,6 +61,8 @@ public class BaseJavaValidator extends org.eclipse.etrice.core.common.validation public static final String DUPLICATE_ANNOTATION_ATTRIBUTE = "BaseJavaValidator.DuplicateAnnotationAttribute"; @Inject ImportUriResolver importUriResolver; + @Inject IGlobalScopeProvider globalScopeProvider; + @Inject IQualifiedNameConverter nameConverter; @Check public void checkDocumentation(Documentation doc) { @@ -183,6 +194,10 @@ public class BaseJavaValidator extends org.eclipse.etrice.core.common.validation @Check public void checkImportedURI(Import imp) { + if(imp.getImportURI() == null) { + return; + } + String uriString = importUriResolver.resolve(imp); if (uriString == null) { warning("could not load referenced model", BasePackage.Literals.IMPORT__IMPORT_URI); @@ -214,4 +229,26 @@ public class BaseJavaValidator extends org.eclipse.etrice.core.common.validation } } + /** + * Check that the imported namespace can be found by the global scope provider. + * + * @param imp the import to check + */ + @Check + public void checkImportedNamespace(Import imp) { + String name = imp.getImportedNamespace(); + if(name != null) { + QualifiedName importedNamespace = nameConverter.toQualifiedName(name); + if(!importedNamespace.getLastSegment().equals("*")) { + Resource context = imp.eResource(); + EReference reference = EcoreFactory.eINSTANCE.createEReference(); + reference.setEType(EcorePackage.eINSTANCE.getEObject()); + IScope scope = globalScopeProvider.getScope(context, reference, Predicates.alwaysTrue()); + IEObjectDescription eod = scope.getSingleElement(importedNamespace); + if(eod == null) { + error("could not find imported namespace " + importedNamespace, BasePackage.Literals.IMPORT__IMPORTED_NAMESPACE); + } + } + } + } } diff --git a/plugins/org.eclipse.etrice.core.config/src-gen/org/eclipse/etrice/core/AbstractConfigRuntimeModule.java b/plugins/org.eclipse.etrice.core.config/src-gen/org/eclipse/etrice/core/AbstractConfigRuntimeModule.java index 9dfe87b6f..536bfef77 100644 --- a/plugins/org.eclipse.etrice.core.config/src-gen/org/eclipse/etrice/core/AbstractConfigRuntimeModule.java +++ b/plugins/org.eclipse.etrice.core.config/src-gen/org/eclipse/etrice/core/AbstractConfigRuntimeModule.java @@ -110,12 +110,12 @@ public abstract class AbstractConfigRuntimeModule extends org.eclipse.xtext.serv // contributed by org.eclipse.xtext.generator.scoping.AbstractScopingFragment public void configureIScopeProviderDelegate(com.google.inject.Binder binder) { - binder.bind(org.eclipse.xtext.scoping.IScopeProvider.class).annotatedWith(com.google.inject.name.Names.named(org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider.NAMED_DELEGATE)).to(org.eclipse.xtext.scoping.impl.SimpleLocalScopeProvider.class); + binder.bind(org.eclipse.xtext.scoping.IScopeProvider.class).annotatedWith(com.google.inject.name.Names.named(org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider.NAMED_DELEGATE)).to(org.eclipse.xtext.scoping.impl.ImportedNamespaceAwareLocalScopeProvider.class); } // contributed by org.eclipse.xtext.generator.scoping.AbstractScopingFragment public Class<? extends org.eclipse.xtext.scoping.IGlobalScopeProvider> bindIGlobalScopeProvider() { - return org.eclipse.xtext.scoping.impl.ImportUriGlobalScopeProvider.class; + return org.eclipse.xtext.scoping.impl.DefaultGlobalScopeProvider.class; } // contributed by org.eclipse.xtext.generator.scoping.AbstractScopingFragment diff --git a/plugins/org.eclipse.etrice.core.config/src/org/eclipse/etrice/core/ConfigRuntimeModule.java b/plugins/org.eclipse.etrice.core.config/src/org/eclipse/etrice/core/ConfigRuntimeModule.java index 300f1f721..b2e9e3a80 100644 --- a/plugins/org.eclipse.etrice.core.config/src/org/eclipse/etrice/core/ConfigRuntimeModule.java +++ b/plugins/org.eclipse.etrice.core.config/src/org/eclipse/etrice/core/ConfigRuntimeModule.java @@ -14,13 +14,15 @@ package org.eclipse.etrice.core; +import org.eclipse.etrice.core.common.scoping.CompoundGlobalScopeProvider; import org.eclipse.etrice.core.common.scoping.ModelLocatorUriResolver; +import org.eclipse.etrice.core.common.scoping.ModelPathFileExtensionFilter; +import org.eclipse.etrice.core.common.scoping.ModelPathGlobalScopeProvider.IModelPathFileFilter; import org.eclipse.etrice.core.converter.ConfigValueConverterService; import org.eclipse.xtext.conversion.IValueConverterService; +import org.eclipse.xtext.scoping.IGlobalScopeProvider; import org.eclipse.xtext.scoping.impl.ImportUriResolver; -import com.google.inject.Binder; - /** * Use this class to register components to be used at runtime / without the * Equinox extension registry. @@ -28,19 +30,13 @@ import com.google.inject.Binder; public class ConfigRuntimeModule extends org.eclipse.etrice.core.AbstractConfigRuntimeModule { - /* - * (non-Javadoc) - * - * @see org.eclipse.etrice.core.AbstractRoomRuntimeModule# - * configureIScopeProviderDelegate(com.google.inject.Binder) - */ @Override - public void configureIScopeProviderDelegate(Binder binder) { - binder.bind(org.eclipse.xtext.scoping.IScopeProvider.class) - .annotatedWith( - com.google.inject.name.Names - .named(org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider.NAMED_DELEGATE)) - .to(org.eclipse.xtext.scoping.impl.ImportedNamespaceAwareLocalScopeProvider.class); + public Class<? extends IGlobalScopeProvider> bindIGlobalScopeProvider() { + return CompoundGlobalScopeProvider.class; + } + + public IModelPathFileFilter bindIModelPathFileFilter() { + return new ModelPathFileExtensionFilter("room"); } // HOWTO: use URI imports - need special URI resolver diff --git a/plugins/org.eclipse.etrice.core.config/src/org/eclipse/etrice/core/GenerateConfig.mwe2 b/plugins/org.eclipse.etrice.core.config/src/org/eclipse/etrice/core/GenerateConfig.mwe2 index 758adc438..c36c68645 100644 --- a/plugins/org.eclipse.etrice.core.config/src/org/eclipse/etrice/core/GenerateConfig.mwe2 +++ b/plugins/org.eclipse.etrice.core.config/src/org/eclipse/etrice/core/GenerateConfig.mwe2 @@ -98,8 +98,8 @@ Workflow { // fragment = exporting.SimpleNamesFragment {} // scoping and exporting API - fragment = scoping.ImportURIScopingFragment {} - // fragment = scoping.ImportNamespacesScopingFragment {} + // fragment = scoping.ImportURIScopingFragment {} + fragment = scoping.ImportNamespacesScopingFragment {} fragment = exporting.QualifiedNamesFragment {} fragment = builder.BuilderIntegrationFragment {} diff --git a/plugins/org.eclipse.etrice.core.etmap/src-gen/org/eclipse/etrice/core/etmap/AbstractETMapRuntimeModule.java b/plugins/org.eclipse.etrice.core.etmap/src-gen/org/eclipse/etrice/core/etmap/AbstractETMapRuntimeModule.java index a8809359c..66c75f680 100644 --- a/plugins/org.eclipse.etrice.core.etmap/src-gen/org/eclipse/etrice/core/etmap/AbstractETMapRuntimeModule.java +++ b/plugins/org.eclipse.etrice.core.etmap/src-gen/org/eclipse/etrice/core/etmap/AbstractETMapRuntimeModule.java @@ -105,12 +105,12 @@ public abstract class AbstractETMapRuntimeModule extends org.eclipse.xtext.servi // contributed by org.eclipse.xtext.generator.scoping.AbstractScopingFragment public void configureIScopeProviderDelegate(com.google.inject.Binder binder) { - binder.bind(org.eclipse.xtext.scoping.IScopeProvider.class).annotatedWith(com.google.inject.name.Names.named(org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider.NAMED_DELEGATE)).to(org.eclipse.xtext.scoping.impl.SimpleLocalScopeProvider.class); + binder.bind(org.eclipse.xtext.scoping.IScopeProvider.class).annotatedWith(com.google.inject.name.Names.named(org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider.NAMED_DELEGATE)).to(org.eclipse.xtext.scoping.impl.ImportedNamespaceAwareLocalScopeProvider.class); } // contributed by org.eclipse.xtext.generator.scoping.AbstractScopingFragment public Class<? extends org.eclipse.xtext.scoping.IGlobalScopeProvider> bindIGlobalScopeProvider() { - return org.eclipse.xtext.scoping.impl.ImportUriGlobalScopeProvider.class; + return org.eclipse.xtext.scoping.impl.DefaultGlobalScopeProvider.class; } // contributed by org.eclipse.xtext.generator.scoping.AbstractScopingFragment diff --git a/plugins/org.eclipse.etrice.core.etmap/src/org/eclipse/etrice/core/etmap/ETMapRuntimeModule.java b/plugins/org.eclipse.etrice.core.etmap/src/org/eclipse/etrice/core/etmap/ETMapRuntimeModule.java index f2e853aae..35fa20333 100644 --- a/plugins/org.eclipse.etrice.core.etmap/src/org/eclipse/etrice/core/etmap/ETMapRuntimeModule.java +++ b/plugins/org.eclipse.etrice.core.etmap/src/org/eclipse/etrice/core/etmap/ETMapRuntimeModule.java @@ -14,23 +14,25 @@ package org.eclipse.etrice.core.etmap; +import org.eclipse.etrice.core.common.scoping.CompoundGlobalScopeProvider; import org.eclipse.etrice.core.common.scoping.ModelLocatorUriResolver; +import org.eclipse.etrice.core.common.scoping.ModelPathFileExtensionFilter; +import org.eclipse.etrice.core.common.scoping.ModelPathGlobalScopeProvider.IModelPathFileFilter; +import org.eclipse.xtext.scoping.IGlobalScopeProvider; import org.eclipse.xtext.scoping.impl.ImportUriResolver; -import com.google.inject.Binder; - /** * Use this class to register components to be used at runtime / without the Equinox extension registry. */ public class ETMapRuntimeModule extends org.eclipse.etrice.core.etmap.AbstractETMapRuntimeModule { @Override - public void configureIScopeProviderDelegate(Binder binder) { - binder.bind(org.eclipse.xtext.scoping.IScopeProvider.class) - .annotatedWith( - com.google.inject.name.Names - .named(org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider.NAMED_DELEGATE)) - .to(org.eclipse.xtext.scoping.impl.ImportedNamespaceAwareLocalScopeProvider.class); + public Class<? extends IGlobalScopeProvider> bindIGlobalScopeProvider() { + return CompoundGlobalScopeProvider.class; + } + + public IModelPathFileFilter bindIModelPathFileFilter() { + return new ModelPathFileExtensionFilter("room", "etphys"); } // HOWTO: use URI imports - need special URI resolver diff --git a/plugins/org.eclipse.etrice.core.etmap/src/org/eclipse/etrice/core/etmap/GenerateETMap.mwe2 b/plugins/org.eclipse.etrice.core.etmap/src/org/eclipse/etrice/core/etmap/GenerateETMap.mwe2 index bdae5cd6c..7f153a7c2 100644 --- a/plugins/org.eclipse.etrice.core.etmap/src/org/eclipse/etrice/core/etmap/GenerateETMap.mwe2 +++ b/plugins/org.eclipse.etrice.core.etmap/src/org/eclipse/etrice/core/etmap/GenerateETMap.mwe2 @@ -95,7 +95,7 @@ Workflow { // fragment = exporting.SimpleNamesFragment {} // scoping and exporting API - fragment = scoping.ImportURIScopingFragment {} + fragment = scoping.ImportNamespacesScopingFragment {} fragment = exporting.QualifiedNamesFragment {} fragment = builder.BuilderIntegrationFragment {} diff --git a/plugins/org.eclipse.etrice.core.etphys/src-gen/org/eclipse/etrice/core/etphys/AbstractETPhysRuntimeModule.java b/plugins/org.eclipse.etrice.core.etphys/src-gen/org/eclipse/etrice/core/etphys/AbstractETPhysRuntimeModule.java index fbf7a87bf..a38ab7982 100644 --- a/plugins/org.eclipse.etrice.core.etphys/src-gen/org/eclipse/etrice/core/etphys/AbstractETPhysRuntimeModule.java +++ b/plugins/org.eclipse.etrice.core.etphys/src-gen/org/eclipse/etrice/core/etphys/AbstractETPhysRuntimeModule.java @@ -110,12 +110,12 @@ public abstract class AbstractETPhysRuntimeModule extends org.eclipse.xtext.serv // contributed by org.eclipse.xtext.generator.scoping.AbstractScopingFragment public void configureIScopeProviderDelegate(com.google.inject.Binder binder) { - binder.bind(org.eclipse.xtext.scoping.IScopeProvider.class).annotatedWith(com.google.inject.name.Names.named(org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider.NAMED_DELEGATE)).to(org.eclipse.xtext.scoping.impl.SimpleLocalScopeProvider.class); + binder.bind(org.eclipse.xtext.scoping.IScopeProvider.class).annotatedWith(com.google.inject.name.Names.named(org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider.NAMED_DELEGATE)).to(org.eclipse.xtext.scoping.impl.ImportedNamespaceAwareLocalScopeProvider.class); } // contributed by org.eclipse.xtext.generator.scoping.AbstractScopingFragment public Class<? extends org.eclipse.xtext.scoping.IGlobalScopeProvider> bindIGlobalScopeProvider() { - return org.eclipse.xtext.scoping.impl.ImportUriGlobalScopeProvider.class; + return org.eclipse.xtext.scoping.impl.DefaultGlobalScopeProvider.class; } // contributed by org.eclipse.xtext.generator.scoping.AbstractScopingFragment diff --git a/plugins/org.eclipse.etrice.core.etphys/src/org/eclipse/etrice/core/etphys/ETPhysRuntimeModule.java b/plugins/org.eclipse.etrice.core.etphys/src/org/eclipse/etrice/core/etphys/ETPhysRuntimeModule.java index 26fd1961b..d9e982a36 100644 --- a/plugins/org.eclipse.etrice.core.etphys/src/org/eclipse/etrice/core/etphys/ETPhysRuntimeModule.java +++ b/plugins/org.eclipse.etrice.core.etphys/src/org/eclipse/etrice/core/etphys/ETPhysRuntimeModule.java @@ -29,13 +29,7 @@ import com.google.inject.Binder; public class ETPhysRuntimeModule extends org.eclipse.etrice.core.etphys.AbstractETPhysRuntimeModule { @SuppressWarnings("restriction") - @Override - public void configureIScopeProviderDelegate(Binder binder) { - binder.bind(org.eclipse.xtext.scoping.IScopeProvider.class) - .annotatedWith( - com.google.inject.name.Names - .named(org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider.NAMED_DELEGATE)) - .to(org.eclipse.xtext.scoping.impl.ImportedNamespaceAwareLocalScopeProvider.class); + public void configureITransientValueService(Binder binder) { binder.bind(org.eclipse.xtext.serializer.sequencer.ITransientValueService.class).to( ETPhysTransientValueService.class); } diff --git a/plugins/org.eclipse.etrice.core.etphys/src/org/eclipse/etrice/core/etphys/GenerateETPhys.mwe2 b/plugins/org.eclipse.etrice.core.etphys/src/org/eclipse/etrice/core/etphys/GenerateETPhys.mwe2 index d219db5bd..de7e3a62c 100644 --- a/plugins/org.eclipse.etrice.core.etphys/src/org/eclipse/etrice/core/etphys/GenerateETPhys.mwe2 +++ b/plugins/org.eclipse.etrice.core.etphys/src/org/eclipse/etrice/core/etphys/GenerateETPhys.mwe2 @@ -91,8 +91,8 @@ Workflow { // fragment = exporting.SimpleNamesFragment {} // scoping and exporting API - fragment = scoping.ImportURIScopingFragment {} - //fragment = scoping.ImportNamespacesScopingFragment {} + // fragment = scoping.ImportURIScopingFragment {} + fragment = scoping.ImportNamespacesScopingFragment {} fragment = exporting.QualifiedNamesFragment {} //fragment = builder.BuilderIntegrationFragment {} diff --git a/plugins/org.eclipse.etrice.core.fsm/src-gen/org/eclipse/etrice/core/fsm/AbstractFSMRuntimeModule.java b/plugins/org.eclipse.etrice.core.fsm/src-gen/org/eclipse/etrice/core/fsm/AbstractFSMRuntimeModule.java index d9ad7cf06..47bc8104f 100644 --- a/plugins/org.eclipse.etrice.core.fsm/src-gen/org/eclipse/etrice/core/fsm/AbstractFSMRuntimeModule.java +++ b/plugins/org.eclipse.etrice.core.fsm/src-gen/org/eclipse/etrice/core/fsm/AbstractFSMRuntimeModule.java @@ -110,12 +110,12 @@ public abstract class AbstractFSMRuntimeModule extends org.eclipse.xtext.service // contributed by org.eclipse.xtext.generator.scoping.AbstractScopingFragment public void configureIScopeProviderDelegate(com.google.inject.Binder binder) { - binder.bind(org.eclipse.xtext.scoping.IScopeProvider.class).annotatedWith(com.google.inject.name.Names.named(org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider.NAMED_DELEGATE)).to(org.eclipse.xtext.scoping.impl.SimpleLocalScopeProvider.class); + binder.bind(org.eclipse.xtext.scoping.IScopeProvider.class).annotatedWith(com.google.inject.name.Names.named(org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider.NAMED_DELEGATE)).to(org.eclipse.xtext.scoping.impl.ImportedNamespaceAwareLocalScopeProvider.class); } // contributed by org.eclipse.xtext.generator.scoping.AbstractScopingFragment public Class<? extends org.eclipse.xtext.scoping.IGlobalScopeProvider> bindIGlobalScopeProvider() { - return org.eclipse.xtext.scoping.impl.ImportUriGlobalScopeProvider.class; + return org.eclipse.xtext.scoping.impl.DefaultGlobalScopeProvider.class; } // contributed by org.eclipse.xtext.generator.scoping.AbstractScopingFragment diff --git a/plugins/org.eclipse.etrice.core.fsm/src/org/eclipse/etrice/core/fsm/GenerateFSM.mwe2 b/plugins/org.eclipse.etrice.core.fsm/src/org/eclipse/etrice/core/fsm/GenerateFSM.mwe2 index 3360b0453..00fae1a4c 100644 --- a/plugins/org.eclipse.etrice.core.fsm/src/org/eclipse/etrice/core/fsm/GenerateFSM.mwe2 +++ b/plugins/org.eclipse.etrice.core.fsm/src/org/eclipse/etrice/core/fsm/GenerateFSM.mwe2 @@ -96,7 +96,7 @@ Workflow { // fragment = exporting.SimpleNamesFragment auto-inject {} // scoping and exporting API - fragment = scoping.ImportURIScopingFragment auto-inject {} + fragment = scoping.ImportNamespacesScopingFragment auto-inject {} fragment = exporting.QualifiedNamesFragment auto-inject {} fragment = builder.BuilderIntegrationFragment auto-inject {} diff --git a/plugins/org.eclipse.etrice.core.room/src-gen/org/eclipse/etrice/core/AbstractRoomRuntimeModule.java b/plugins/org.eclipse.etrice.core.room/src-gen/org/eclipse/etrice/core/AbstractRoomRuntimeModule.java index 6d84646be..2d1125d68 100644 --- a/plugins/org.eclipse.etrice.core.room/src-gen/org/eclipse/etrice/core/AbstractRoomRuntimeModule.java +++ b/plugins/org.eclipse.etrice.core.room/src-gen/org/eclipse/etrice/core/AbstractRoomRuntimeModule.java @@ -110,12 +110,12 @@ public abstract class AbstractRoomRuntimeModule extends org.eclipse.xtext.servic // contributed by org.eclipse.xtext.generator.scoping.AbstractScopingFragment public void configureIScopeProviderDelegate(com.google.inject.Binder binder) { - binder.bind(org.eclipse.xtext.scoping.IScopeProvider.class).annotatedWith(com.google.inject.name.Names.named(org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider.NAMED_DELEGATE)).to(org.eclipse.xtext.scoping.impl.SimpleLocalScopeProvider.class); + binder.bind(org.eclipse.xtext.scoping.IScopeProvider.class).annotatedWith(com.google.inject.name.Names.named(org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider.NAMED_DELEGATE)).to(org.eclipse.xtext.scoping.impl.ImportedNamespaceAwareLocalScopeProvider.class); } // contributed by org.eclipse.xtext.generator.scoping.AbstractScopingFragment public Class<? extends org.eclipse.xtext.scoping.IGlobalScopeProvider> bindIGlobalScopeProvider() { - return org.eclipse.xtext.scoping.impl.ImportUriGlobalScopeProvider.class; + return org.eclipse.xtext.scoping.impl.DefaultGlobalScopeProvider.class; } // contributed by org.eclipse.xtext.generator.scoping.AbstractScopingFragment diff --git a/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/GenerateRoom.mwe2 b/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/GenerateRoom.mwe2 index 793ff9ca7..000c851d5 100644 --- a/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/GenerateRoom.mwe2 +++ b/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/GenerateRoom.mwe2 @@ -112,7 +112,7 @@ Workflow { */ // scoping and exporting API - fragment = scoping.ImportURIScopingFragment {} + fragment = scoping.ImportNamespacesScopingFragment {} fragment = exporting.QualifiedNamesFragment {} fragment = builder.BuilderIntegrationFragment {} diff --git a/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/RoomRuntimeModule.java b/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/RoomRuntimeModule.java index 515378aa1..5cd4d0400 100644 --- a/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/RoomRuntimeModule.java +++ b/plugins/org.eclipse.etrice.core.room/src/org/eclipse/etrice/core/RoomRuntimeModule.java @@ -14,7 +14,10 @@ package org.eclipse.etrice.core; +import org.eclipse.etrice.core.common.scoping.CompoundGlobalScopeProvider; import org.eclipse.etrice.core.common.scoping.ModelLocatorUriResolver; +import org.eclipse.etrice.core.common.scoping.ModelPathFileExtensionFilter; +import org.eclipse.etrice.core.common.scoping.ModelPathGlobalScopeProvider.IModelPathFileFilter; import org.eclipse.etrice.core.common.validation.CustomValidatorManager.StandaloneValidatorExtension; import org.eclipse.etrice.core.converter.RoomValueConverterService; import org.eclipse.etrice.core.genmodel.fsm.ICommonDataCalculator; @@ -27,6 +30,7 @@ import org.eclipse.etrice.core.validation.ValidatorExtensionManager; import org.eclipse.xtext.conversion.IValueConverterService; import org.eclipse.xtext.naming.IQualifiedNameProvider; import org.eclipse.xtext.resource.IFragmentProvider; +import org.eclipse.xtext.scoping.IGlobalScopeProvider; import org.eclipse.xtext.scoping.impl.ImportUriResolver; import org.eclipse.xtext.validation.INamesAreUniqueValidationHelper; @@ -74,20 +78,18 @@ public class RoomRuntimeModule extends org.eclipse.etrice.core.AbstractRoomRunti return RoomFragmentProvider.class; } - /* (non-Javadoc) - * @see org.eclipse.etrice.core.AbstractRoomRuntimeModule#configureIScopeProviderDelegate(com.google.inject.Binder) - */ - @Override - public void configureIScopeProviderDelegate(Binder binder) { - binder.bind(org.eclipse.xtext.scoping.IScopeProvider.class).annotatedWith( - com.google.inject.name.Names.named(org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider.NAMED_DELEGATE)).to( - org.eclipse.xtext.scoping.impl.ImportedNamespaceAwareLocalScopeProvider.class); - } - // HOWTO: use URI imports - need special URI resolver public Class<? extends ImportUriResolver> bindImportUriResolver() { return ModelLocatorUriResolver.class; } + + public Class<? extends IGlobalScopeProvider> bindIGlobalScopeProvider() { + return CompoundGlobalScopeProvider.class; + } + + public IModelPathFileFilter bindIModelPathFileFilter() { + return new ModelPathFileExtensionFilter("room", "cage", "actortest"); + } // HOWTO: add a value converter @Override diff --git a/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/GeneratorApplication.java b/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/GeneratorApplication.java index 4d17fe86e..bc705bc0f 100644 --- a/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/GeneratorApplication.java +++ b/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/GeneratorApplication.java @@ -207,7 +207,8 @@ public class GeneratorApplication { private List<Resource> load(Arguments arguments, Logger logger) { List<String> files = Arrays.asList(arguments.get(GeneratorApplicationOptions.FILES)); - return resourceLoader.load(files, arguments, logger); + List<String> modelpath = Arrays.asList(arguments.get(GeneratorApplicationOptions.MODELPATH)); + return resourceLoader.load(files, modelpath, arguments, logger); } private void validate(List<Resource> models, Arguments arguments, Logger logger) { diff --git a/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/args/Arguments.java b/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/args/Arguments.java index 4557d3731..56a0aca60 100644 --- a/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/args/Arguments.java +++ b/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/args/Arguments.java @@ -15,7 +15,9 @@ package org.eclipse.etrice.generator.base.args; +import java.util.Arrays; import java.util.HashMap; +import java.util.StringJoiner; /** * Encapsulates a set of arguments. @@ -98,6 +100,23 @@ public class Arguments { @Override public String toString() { - return option2Arg.toString(); + StringJoiner joiner = new StringJoiner(", "); + for(Option<?> opt: options) { + joiner.add(argToString(opt)); + } + return joiner.toString(); + } + + private String argToString(Option<?> opt) { + String result = opt.getName() + "="; + Object obj = option2Arg.get(opt.getName()); + if(opt.getType().isArray()) { + Object[] objArr = (Object[]) obj; + result += Arrays.toString(objArr); + } + else { + result += obj.toString(); + } + return result; } } diff --git a/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/io/FileSystemModelPath.java b/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/io/FileSystemModelPath.java new file mode 100644 index 000000000..995027399 --- /dev/null +++ b/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/io/FileSystemModelPath.java @@ -0,0 +1,139 @@ +/******************************************************************************* +* Copyright (c) 2018 protos software gmbh (http://www.protos.de). +* All rights reserved. +* +* 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/ +* +* SPDX-License-Identifier: EPL-2.0 +* +* CONTRIBUTORS: +* Jan Belle (initial contribution) +* + *******************************************************************************/ + +package org.eclipse.etrice.generator.base.io; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.xtext.naming.QualifiedName; + +/** + * Modelpath implementation based on the file system. + */ +public class FileSystemModelPath implements IModelPath { + + private List<Path> paths; + private HashMap<QualifiedName, List<URI>> packages; + + /** + * Creates a new modelpath that contains all files in the passed paths. + * + * @param paths the paths to search for files + */ + public FileSystemModelPath(List<Path> paths) { + this.paths = paths; + this.packages = new HashMap<>(); + } + + @Override + public Stream<URI> getFiles(QualifiedName name) { + Stream<URI> stream = Stream.empty(); + while(!name.isEmpty()) { + name = name.skipLast(1); + stream = Stream.concat(stream, getPackage(name)); + } + return stream; + } + + @Override + public Stream<URI> getAllFiles() { + throw new UnsupportedOperationException(); + } + + @Override + public String toString() { + return packages.toString(); + } + + /** + * Returns all files that are contained in the package with the specified name. + * + * @param name the fully qualified name of the package + * @return a stream of all file uris + */ + private Stream<URI> getPackage(QualifiedName name) { + List<URI> pkg = packages.computeIfAbsent(name, n -> createPackage(n)); + return pkg.stream(); + } + + /** + * Searches for all files that are contained in the package with the specified name. + * + * @param name the fully qualified name of the package + * @return a list of all file uris + */ + private List<URI> createPackage(QualifiedName name) { + return paths.stream() + .map(path -> createPackagePath(name, path)) + .filter(path -> Files.isDirectory(path, LinkOption.NOFOLLOW_LINKS)) + .flatMap(dir -> collectFiles(dir)) + .collect(Collectors.toList()); + } + + /** + * Lists all files of the passed directory. + * + * @param dir the path of a folder + * @return a stream of file uris + */ + private Stream<URI> collectFiles(Path dir) { + try { + return Files.list(dir) + .filter(path -> Files.isRegularFile(path)) + .map(file -> createURI(file)); + } + catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + /** + * Creates an uri for a given path. + */ + private URI createURI(Path path) { + return URI.createURI(path.toUri().toString()); + } + + /** + * Creates the path of a specific package within the passed directory. + * + * @param name the fully qualified name of the package + * @param dir the folder which the package path is based on + * @return the path of the package + */ + private Path createPackagePath(QualifiedName name, Path dir) { + if(!name.isEmpty()) { + String firstSegment = name.getFirstSegment(); + String[] segments = new String[name.getSegmentCount() - 1]; + for(int i = 0; i < segments.length; ++i) { + segments[i] = name.getSegment(i + 1); + } + Path relativePackagePath = dir.getFileSystem().getPath(firstSegment, segments); + Path packagePath = dir.resolve(relativePackagePath); + return packagePath; + } + return dir; + } + +} diff --git a/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/io/GeneratorResourceLoader.java b/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/io/GeneratorResourceLoader.java index 0a59f8bed..73768ee7b 100644 --- a/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/io/GeneratorResourceLoader.java +++ b/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/io/GeneratorResourceLoader.java @@ -16,7 +16,14 @@ package org.eclipse.etrice.generator.base.io; import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.ProviderNotFoundException; import java.util.ArrayList; import java.util.List; @@ -55,18 +62,17 @@ public class GeneratorResourceLoader implements IGeneratorResourceLoader { } @Override - public List<Resource> load(List<String> files, Arguments arguments, ILogger logger) throws GeneratorException { + public List<Resource> load(List<String> files, List<String> modelpath, Arguments arguments, ILogger logger) throws GeneratorException { doEMFRegistration(); - List<Resource> resources = new ArrayList<>(files.size()); ResourceSet resourceSet = resourceSetProvider.get(); Adapter resourceAddedAdapter = new ResourceAddedAdapter(logger); resourceSet.eAdapters().add(resourceAddedAdapter); - - for(String f: files) { - Resource r = loadResource(f, resourceSet, logger); - resources.add(r); - } + + IModelPath mp = createModelPath(modelpath, logger); + resourceSet.getLoadOptions().putIfAbsent(ResourceSetModelPathProvider.LOAD_OPTION_MODELPATH, mp); + + List<Resource> resources = loadResources(files, resourceSet, logger); EcoreUtil.resolveAll(resourceSet); @@ -80,21 +86,83 @@ public class GeneratorResourceLoader implements IGeneratorResourceLoader { } } + private List<Resource> loadResources(List<String> files, ResourceSet resourceSet, ILogger logger) { + List<Resource> resources = new ArrayList<>(files.size()); + for(String f: files) { + Resource r = loadResource(f, resourceSet, logger); + resources.add(r); + } + return resources; + } + private Resource loadResource(String file, ResourceSet rs, ILogger logger) { + Path normalizedPath = Paths.get(file).normalize(); + if(Files.exists(normalizedPath)) { + Path realPath = toRealPath(normalizedPath); + URI uri = createURI(realPath); + try { + return rs.getResource(uri, true); + } + catch(RuntimeException e) { + logger.logError("could not load file " + file + "; " + e.getMessage()); + throw new GeneratorException(e); + } + } + else { + logger.logError("could not find file " + file); + throw new GeneratorException(); + } + } + + private IModelPath createModelPath(List<String> pathStrings, ILogger logger) { + ArrayList<Path> paths = new ArrayList<>(pathStrings.size()); + for(String pathString: pathStrings) { + Path path = Paths.get(pathString).normalize(); + if(Files.exists(path)) { + Path realPath = toRealPath(path); + if(Files.isDirectory(realPath)) { + paths.add(realPath); + } + else if(Files.isRegularFile(realPath)) { + FileSystem fileSystem = getFileSystem(realPath, logger); + for(Path rootDir: fileSystem.getRootDirectories()) { + paths.add(rootDir); + } + } + } + else { + logger.logWarning("could not find modelpath entry " + pathString); + } + } + + FileSystemModelPath modelpath = new FileSystemModelPath(paths); + return modelpath; + } + + private FileSystem getFileSystem(Path path, ILogger logger) { try { - URI uri = createURI(file); - return rs.getResource(uri, true); + return FileSystems.newFileSystem(path, null); } - catch(RuntimeException | IOException e) { - logger.logError("couldn't load file " + file + "; " + e.getMessage()); + catch(ProviderNotFoundException e) { + logger.logError("could not read modelpath entry " + path.toString()); throw new GeneratorException(e); } + catch(IOException e) { + throw new UncheckedIOException(e); + } } - protected URI createURI(String file) throws IOException { - String realPath = Paths.get(file).toRealPath().toString(); - URI uri = URI.createFileURI(realPath); - return uri; + protected URI createURI(Path path) { + return URI.createURI(path.toUri().toString()); + } + + private Path toRealPath(Path path, LinkOption... options) { + try { + return path.toRealPath(options); + } + catch(IOException e) { + throw new UncheckedIOException(e); + } } private class ResourceAddedAdapter extends AdapterImpl { diff --git a/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/io/IGeneratorResourceLoader.java b/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/io/IGeneratorResourceLoader.java index 3fbf77169..3b0d54398 100644 --- a/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/io/IGeneratorResourceLoader.java +++ b/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/io/IGeneratorResourceLoader.java @@ -34,10 +34,11 @@ public interface IGeneratorResourceLoader { * Loads the specified files. * * @param files the files to load + * @param modelpath the modelpath entries * @param arguments the generator arguments * @param logger the logger * @return the loaded resources */ - List<Resource> load(List<String> files, Arguments arguments, ILogger logger) throws GeneratorException; + List<Resource> load(List<String> files, List<String> modelpath, Arguments arguments, ILogger logger) throws GeneratorException; } diff --git a/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/io/IModelPath.java b/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/io/IModelPath.java new file mode 100644 index 000000000..7ad76417d --- /dev/null +++ b/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/io/IModelPath.java @@ -0,0 +1,54 @@ +/******************************************************************************* +* Copyright (c) 2018 protos software gmbh (http://www.protos.de). +* All rights reserved. +* +* 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/ +* +* SPDX-License-Identifier: EPL-2.0 +* +* CONTRIBUTORS: +* Jan Belle (initial contribution) +* + *******************************************************************************/ + +package org.eclipse.etrice.generator.base.io; + +import java.util.stream.Stream; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.xtext.naming.QualifiedName; + +public interface IModelPath { + + public static final IModelPath EMPTY = new EmptyModelPath(); + + /** + * Returns all files that could contain the object with the specified name. + * + * @param name the fully qualified name of the object + * @return a stream of file uris + */ + public Stream<URI> getFiles(QualifiedName name); + + /** + * Returns all files on this modelpath. + * + * @return a stream of file uris + */ + public Stream<URI> getAllFiles(); + + static class EmptyModelPath implements IModelPath { + + @Override + public Stream<URI> getFiles(QualifiedName name) { + return Stream.empty(); + } + + @Override + public Stream<URI> getAllFiles() { + return Stream.empty(); + } + } +} diff --git a/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/io/IModelPathProvider.java b/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/io/IModelPathProvider.java new file mode 100644 index 000000000..a6b091172 --- /dev/null +++ b/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/io/IModelPathProvider.java @@ -0,0 +1,33 @@ +/******************************************************************************* +* Copyright (c) 2018 protos software gmbh (http://www.protos.de). +* All rights reserved. +* +* 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/ +* +* SPDX-License-Identifier: EPL-2.0 +* +* CONTRIBUTORS: +* Jan Belle (initial contribution) +* + *******************************************************************************/ + +package org.eclipse.etrice.generator.base.io; + +import org.eclipse.emf.ecore.resource.Resource; + +import com.google.inject.ImplementedBy; + +@ImplementedBy(ResourceSetModelPathProvider.class) +public interface IModelPathProvider { + + /** + * Requests the modelpath for the specified resource. + * + * @param resource the resource that requested modelpath is for + * @return the modelpath for the resource + */ + public IModelPath get(Resource resource); + +} diff --git a/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/io/ResourceSetModelPathProvider.java b/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/io/ResourceSetModelPathProvider.java new file mode 100644 index 000000000..0bc4fc72c --- /dev/null +++ b/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/io/ResourceSetModelPathProvider.java @@ -0,0 +1,38 @@ +/******************************************************************************* +* Copyright (c) 2018 protos software gmbh (http://www.protos.de). +* All rights reserved. +* +* 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/ +* +* SPDX-License-Identifier: EPL-2.0 +* +* CONTRIBUTORS: +* Jan Belle (initial contribution) +* + *******************************************************************************/ + +package org.eclipse.etrice.generator.base.io; + +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; + +/** + * Retrieves the modelpath from the load options of the underlying resource set. + */ +public class ResourceSetModelPathProvider implements IModelPathProvider { + + public static final String LOAD_OPTION_MODELPATH = "org.eclipse.etrice.generator.base.modelpath"; + + @Override + public IModelPath get(Resource resource) { + ResourceSet resourceSet = resource.getResourceSet(); + Object modelpath = resourceSet.getLoadOptions().get(LOAD_OPTION_MODELPATH); + if(modelpath != null && modelpath instanceof IModelPath) { + return (IModelPath) modelpath; + } + return IModelPath.EMPTY; + } + +}
\ No newline at end of file diff --git a/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/setup/GeneratorApplicationOptions.java b/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/setup/GeneratorApplicationOptions.java index b77ac3042..31e1f0882 100644 --- a/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/setup/GeneratorApplicationOptions.java +++ b/plugins/org.eclipse.etrice.generator.base/src/org/eclipse/etrice/generator/base/setup/GeneratorApplicationOptions.java @@ -39,6 +39,13 @@ public class GeneratorApplicationOptions implements IOptionModule { "input files for the generator", new String[0]); + public static final StringArrayOption MODELPATH = new StringArrayOption( + GROUP_APPLICATION, + "modelpath", + "paths", + "model imported paths separated by ';'", + new String[0]); + public static final BooleanOption HELP = new BooleanOption( GROUP_APPLICATION, "help", @@ -87,6 +94,7 @@ public class GeneratorApplicationOptions implements IOptionModule { @Override public void configure(List<Option<?>> options) { options.add(FILES); + options.add(MODELPATH); options.add(HELP); options.add(GEN_INCREMENTAL); options.add(GEN_DIR); diff --git a/plugins/org.eclipse.etrice.generator.launch/META-INF/MANIFEST.MF b/plugins/org.eclipse.etrice.generator.launch/META-INF/MANIFEST.MF index 2daa273f8..2c496aafc 100644 --- a/plugins/org.eclipse.etrice.generator.launch/META-INF/MANIFEST.MF +++ b/plugins/org.eclipse.etrice.generator.launch/META-INF/MANIFEST.MF @@ -19,7 +19,8 @@ Require-Bundle: org.eclipse.etrice.generator;bundle-version="2.0.0", org.eclipse.core.variables, org.eclipse.jdt.launching, org.eclipse.ui.ide, - org.eclipse.etrice.generator.base;bundle-version="2.0.0" + org.eclipse.etrice.generator.base;bundle-version="2.0.0", + org.eclipse.etrice.core.common.ui Import-Package: org.eclipse.xtext.xbase.lib Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-1.8 diff --git a/plugins/org.eclipse.etrice.generator.launch/src/org/eclipse/etrice/generator/launch/GeneratorLaunchConfigurationDelegate.java b/plugins/org.eclipse.etrice.generator.launch/src/org/eclipse/etrice/generator/launch/GeneratorLaunchConfigurationDelegate.java index 0fe8edf3d..96adb23a3 100644 --- a/plugins/org.eclipse.etrice.generator.launch/src/org/eclipse/etrice/generator/launch/GeneratorLaunchConfigurationDelegate.java +++ b/plugins/org.eclipse.etrice.generator.launch/src/org/eclipse/etrice/generator/launch/GeneratorLaunchConfigurationDelegate.java @@ -20,6 +20,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IProject; @@ -36,6 +37,8 @@ import org.eclipse.core.variables.VariablesPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.ui.RefreshTab; +import org.eclipse.etrice.core.common.ui.modelpath.ModelPathManager; +import org.eclipse.etrice.core.common.ui.modelpath.WorkspaceModelPath; import org.eclipse.etrice.generator.base.AbstractGeneratorOptions; import org.eclipse.etrice.generator.base.io.ILineOutput; import org.eclipse.etrice.generator.base.setup.GeneratorApplicationOptions; @@ -88,9 +91,10 @@ public abstract class GeneratorLaunchConfigurationDelegate extends AbstractJavaL StringBuffer argString = new StringBuffer(); addModels(configuration, entry.getValue(), argString); addArguments(configuration, entry.getKey(), argString); + addModelpath(entry.getKey(), argString); String[] args = splitCommandLine(argString.toString()); - output.println("\n*** generating project " + entry.getKey().getName() + " ***"); + output.println("\n*** generating project " + entry.getKey().getName() + " ***\n"); runGenerator(args, output); // check for cancellation @@ -246,6 +250,23 @@ public abstract class GeneratorLaunchConfigurationDelegate extends AbstractJavaL } /** + * Parses the modelpath of the specified project and appends it to the generator arguments. + */ + protected void addModelpath(IProject project, StringBuffer argString) throws CoreException { + WorkspaceModelPath modelpath = ModelPathManager.INSTANCE.getModelPath(project); + String[] paths = modelpath.getPaths().stream() + .map(container -> container.getLocation()) + .filter(Objects::nonNull) + .map(path -> path.toOSString()) + .toArray(size -> new String[size]); + + if(paths.length > 0) { + String modelpathArg = String.join(";", paths); + argString.append(" -modelpath \"").append(modelpathArg).append('"'); + } + } + + /** * split at single spaces but keep strings in double quotes as single argument * (with double quotes removed) */ diff --git a/plugins/org.eclipse.etrice.generator/src/org/eclipse/etrice/generator/base/ModelLoader.java b/plugins/org.eclipse.etrice.generator/src/org/eclipse/etrice/generator/base/ModelLoader.java index 556410078..1e7735a8c 100644 --- a/plugins/org.eclipse.etrice.generator/src/org/eclipse/etrice/generator/base/ModelLoader.java +++ b/plugins/org.eclipse.etrice.generator/src/org/eclipse/etrice/generator/base/ModelLoader.java @@ -14,6 +14,8 @@ package org.eclipse.etrice.generator.base; +import java.nio.file.Path; + import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.etrice.core.common.scoping.ModelLocatorUriResolver; @@ -38,8 +40,8 @@ public class ModelLoader extends GeneratorResourceLoader { } @Override - protected URI createURI(String file) { - String uri = uriResolver.resolve(file, null); + protected URI createURI(Path path) { + String uri = uriResolver.resolve(path.toString(), null); return URI.createURI(uri); } } diff --git a/plugins/org.eclipse.etrice.ui.behavior.fsm/src/org/eclipse/etrice/ui/behavior/fsm/editor/AbstractFSMDiagramBehavior.java b/plugins/org.eclipse.etrice.ui.behavior.fsm/src/org/eclipse/etrice/ui/behavior/fsm/editor/AbstractFSMDiagramBehavior.java index 92b90188e..e71e8e8c4 100644 --- a/plugins/org.eclipse.etrice.ui.behavior.fsm/src/org/eclipse/etrice/ui/behavior/fsm/editor/AbstractFSMDiagramBehavior.java +++ b/plugins/org.eclipse.etrice.ui.behavior.fsm/src/org/eclipse/etrice/ui/behavior/fsm/editor/AbstractFSMDiagramBehavior.java @@ -15,6 +15,7 @@ package org.eclipse.etrice.ui.behavior.fsm.editor; import org.eclipse.etrice.ui.common.base.editor.CustomDiagramBehavior; +import org.eclipse.etrice.ui.common.base.editor.CustomUpdateBehavior; import org.eclipse.gef.EditPart; import org.eclipse.graphiti.ui.editor.DefaultRefreshBehavior; import org.eclipse.graphiti.ui.editor.DefaultUpdateBehavior; @@ -75,7 +76,7 @@ public class AbstractFSMDiagramBehavior extends CustomDiagramBehavior { return new RefreshBehavior(this); } - static class UpdateBehavior extends DefaultUpdateBehavior { + static class UpdateBehavior extends CustomUpdateBehavior { public UpdateBehavior(DiagramBehavior diagramBehavior) { super(diagramBehavior); } diff --git a/plugins/org.eclipse.etrice.ui.common.base/src/org/eclipse/etrice/ui/common/base/editor/CustomResourceSetProvider.java b/plugins/org.eclipse.etrice.ui.common.base/src/org/eclipse/etrice/ui/common/base/editor/CustomResourceSetProvider.java deleted file mode 100644 index 5df3a897b..000000000 --- a/plugins/org.eclipse.etrice.ui.common.base/src/org/eclipse/etrice/ui/common/base/editor/CustomResourceSetProvider.java +++ /dev/null @@ -1,126 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2013 protos software gmbh (http://www.protos.de). - * 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: - * Henrik Rentz-Reichert (initial contribution) - * - *******************************************************************************/ - -package org.eclipse.etrice.ui.common.base.editor; - -import static com.google.common.collect.Maps.newHashMap; - -import java.util.HashMap; -import java.util.Map; - -import org.apache.log4j.Logger; -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IStorage; -import org.eclipse.core.runtime.IPath; -import org.eclipse.emf.common.util.URI; -import org.eclipse.emf.ecore.plugin.EcorePlugin; -import org.eclipse.emf.ecore.resource.ResourceSet; -import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.jdt.core.IPackageFragmentRoot; -import org.eclipse.jdt.core.JavaCore; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.xtext.resource.XtextResourceSet; -import org.eclipse.xtext.ui.resource.IResourceSetProvider; -import org.eclipse.xtext.ui.resource.IStorage2UriMapperJdtExtensions; -import org.eclipse.xtext.ui.util.JdtClasspathUriResolver; -import org.eclipse.xtext.util.Pair; - -import com.google.inject.Inject; -import com.google.inject.Provider; - -/** - * This class is copied from XtextResourceSetProvider with the only modification that we skip our own project in - * the platform project mappings (marked with <b>[HRR]</b>) - * - * @author Henrik Rentz-Reichert - */ -public class CustomResourceSetProvider implements IResourceSetProvider { - - private final static Logger LOG = Logger.getLogger(CustomResourceSetProvider.class); - - @Inject - private Provider<XtextResourceSet> resourceSetProvider; - - @Inject - private IStorage2UriMapperJdtExtensions storage2UriMapper; - - public ResourceSet get(IProject project) { - XtextResourceSet set = resourceSetProvider.get(); - IJavaProject javaProject = JavaCore.create(project); - if (javaProject != null && javaProject.exists()) { - set.getURIConverter().getURIMap().putAll(computePlatformURIMap(javaProject)); - set.setClasspathURIContext(javaProject); - set.setClasspathUriResolver(new JdtClasspathUriResolver()); - } - return set; - } - - protected Map<URI, URI> computePlatformURIMap(IJavaProject javaProject) { - HashMap<URI, URI> hashMap = newHashMap(); - try { - hashMap.putAll(EcorePlugin.computePlatformURIMap(false)); - } catch (Exception e) { - LOG.error(e.getMessage(), e); - } - if (!javaProject.exists()) - return hashMap; - try { - IPackageFragmentRoot[] roots = javaProject.getAllPackageFragmentRoots(); - for (IPackageFragmentRoot root : roots) { - Pair<URI, URI> uriMapping = storage2UriMapper.getURIMapping(root); - if (uriMapping != null) { - - // we could just install the prefix mapping, i.e. platform:resource/my.project/ -> file:/my/path/to/my.project/ - // but then we wouldn't be able to load resources when using hosted bundles, because the target path points to the bin folder. - // so instead we install qualified file mappings, which also makes normalization faster (i.e. just a lookup in a map instead of testing prefix URIs) - // - Map<URI, IStorage> mapping = storage2UriMapper.getAllEntries(root); - for (URI key : mapping.keySet()) { - IStorage storage = mapping.get(key); - URI physicalURI = null; - if (storage instanceof IFile) { - physicalURI = URI.createPlatformResourceURI(storage.getFullPath().toString(), true); - } else { - physicalURI = key.replacePrefix(uriMapping.getFirst(), uriMapping.getSecond()); - } - hashMap.put(key, physicalURI); - if (key.isPlatformResource()) { - URI pluginURI = URI.createPlatformPluginURI(key.toPlatformString(false), false); - hashMap.put(pluginURI, physicalURI); - } - } - } - } - /* - */ - final IProject project = javaProject.getProject(); - for (IProject iProject : project.getWorkspace().getRoot().getProjects()) { - // [HRR]: following line was: - // if (iProject.isAccessible()) { - if (iProject!=project && iProject.isAccessible()) { - IPath location = iProject.getLocation(); - if (location != null) { - // append a trailing slash so that URI.isPrefix is true. - hashMap.put(URI.createPlatformResourceURI(iProject.getName()+"/", true), URI.createFileURI(location.toFile().getPath()+"/")); - } - } - } - } catch (JavaModelException e) { - LOG.error(e.getMessage(), e); - } - return hashMap; - } - - } diff --git a/plugins/org.eclipse.etrice.ui.common.base/src/org/eclipse/etrice/ui/common/base/editor/CustomUpdateBehavior.java b/plugins/org.eclipse.etrice.ui.common.base/src/org/eclipse/etrice/ui/common/base/editor/CustomUpdateBehavior.java index afca52e2b..ff82569c4 100644 --- a/plugins/org.eclipse.etrice.ui.common.base/src/org/eclipse/etrice/ui/common/base/editor/CustomUpdateBehavior.java +++ b/plugins/org.eclipse.etrice.ui.common.base/src/org/eclipse/etrice/ui/common/base/editor/CustomUpdateBehavior.java @@ -33,6 +33,7 @@ import org.eclipse.graphiti.ui.internal.editor.GFWorkspaceCommandStackImpl; import org.eclipse.xtext.resource.XtextResourceSet; import org.eclipse.xtext.ui.resource.IResourceSetProvider; +import com.google.inject.Inject; import com.google.inject.Injector; /** @@ -42,7 +43,8 @@ import com.google.inject.Injector; @SuppressWarnings("restriction") public class CustomUpdateBehavior extends DefaultUpdateBehavior { - private IResourceSetProvider resourceSetProvider = new CustomResourceSetProvider(); + @Inject + private IResourceSetProvider resourceSetProvider; /** * @param diagramBehavior @@ -51,7 +53,7 @@ public class CustomUpdateBehavior extends DefaultUpdateBehavior { super(diagramBehavior); Injector injector = FSMUiModule.getInjector(); - injector.injectMembers(resourceSetProvider); + injector.injectMembers(this); } /* (non-Javadoc) |