Bug 440869 - [MEW] Broken project registry with nested projects
Summary: [MEW] Broken project registry with nested projects
Status: NEW
Alias: None
Product: TMF
Classification: Modeling
Component: Xtext (show other bugs)
Version: 2.5.3   Edit
Hardware: PC Linux-GTK
: P3 major (vote)
Target Milestone: ---   Edit
Assignee: Project Inbox CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2014-07-31 06:41 EDT by Reiner Jung CLA
Modified: 2014-08-04 04:13 EDT (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Reiner Jung CLA 2014-07-31 06:41:32 EDT
I am writing an headless code generator based on Xtext utilizing a code pattern provided by http://davehofmann.de/blog/?tag=standalone

The code in question is as follows:
final org.eclipse.emf.mwe.utils.StandaloneSetup setup = new org.eclipse.emf.mwe.utils.StandaloneSetup();
setup.setPlatformUri(platformUri);

final Injector injector = new RecordLangStandaloneSetup().createInjectorAndDoEMFRegistration();
injector.injectMembers(this);
this.resourceSet.addLoadOption(XtextResource.OPTION_RESOLVE_ALL, Boolean.TRUE);

The setup.setPlatformUri(platformUri) sets the base path where all projects can be found in. In addition the setter scans this path and all its sub path for bundles (jar files) and .project files. While I assumed that it registers all projects and all bundles with EcorePlugin.getPlatformResourceMap().put(name, uri); it does this only for all bundles and the FIRST .project file. 

While this works when multiple projects reside in the same folder, e.g.,

base-path/Project A
base-path/Project B

It does not work with nested projects, e.g.,

base-path/Project A/
base-path/Project A/examples/demo/

The core problem is located in org.eclipse.emf.mwe.utils.StandaloneSetup in the line marked by the comment:

protected boolean scanFolder(File f, Set<String> visitedPathes) {
[...]
	File[] files = f.listFiles();
	boolean containsProject = false;
	File dotProject = null;
	if (files != null) {
		for (File file : files) {
			if (file.exists() && file.isDirectory() && !file.getName().startsWith(".")) {
				containsProject |= scanFolder(file, visitedPathes);
			}
			else if (".project".equals(file.getName())) {
				// instead of remembering the file name, it should call registerProject(file)
				dotProject = file;
			}
			else if (file.getName().endsWith(".jar")) {
				registerBundle(file);
			}
		}
	}
	if (!containsProject && dotProject != null)
		registerProject(dotProject);
	return containsProject || dotProject != null;
}

Alternatively, it could be the intention to only register the most parent projects. However, the present code does not achieve this either. Instead it registers the most nested projects.
Comment 1 Sebastian Zarnekow CLA 2014-07-31 07:47:25 EDT
Nested projects are not really well supported in Eclipse either. I don't think we'll do much about this. If you have nested folders, you can set the platformURI multiple times to dive into those.
Comment 2 Reiner Jung CLA 2014-07-31 09:04:41 EDT
I can understand that. Especially, as nested projects do not make much sense. However, the present behavior bears unpredictable results in such setups. And these setups are present in some OSS projects. Therefore, I propose to fix this with the following minor adjustment.

Instead of
	[...]
	if (file.exists() && file.isDirectory() && !file.getName().startsWith(".")) {
		containsProject |= scanFolder(file, visitedPathes);
	}
	else if (".project".equals(file.getName())) {
		dotProject = file;
	}
	else if (file.getName().endsWith(".jar")) {
		registerBundle(file);
	}
}
if (!containsProject && dotProject != null)
	registerProject(dotProject);

we could use
	[...]
	if (file.exists() && file.isDirectory() && !file.getName().startsWith(".")) {
		containsProject |= scanFolder(file, visitedPathes);
	}
	else if (".project".equals(file.getName())) {
		registerProject(dotProject);
	}
	else if (file.getName().endsWith(".jar")) {
		registerBundle(file);
	}
}
Comment 3 Sebastian Zarnekow CLA 2014-07-31 09:10:49 EDT
(In reply to Reiner Jung from comment #2)
> I can understand that. Especially, as nested projects do not make much
> sense. However, the present behavior bears unpredictable results in such
> setups. 

I don't think the results are unpredicable but rather very well defined. The outermost project will win.

Since there's a workaround / suggested better setup to announce the project directories directly to the standalone-setup, I'm not sure what the problem is. Your suggested solution will scan many more folders unnecessarily for .project files (basically all setups without nested projects, in case of Xtext thousands of folders) and thereby slow down setups that used to work fine. I don't think that everybody should pay the price for some obscure project layouts / workflow configurations.
Comment 4 Reiner Jung CLA 2014-07-31 11:26:48 EDT
It looks to me that both implementations scan the whole directory tree. The difference is in which projects get registered and which are ignored. While the original implementation ignores parent projects, mine will include them. If such project layout is not present in Xtext, it will not make any difference there.

I further do not see that getting only a subset of projects instead of all projects is an easy to understand result of the routine. 

Anyway, for my personal problem I realized my one StandaloneSetup which will only find one project or all projects directly inside the platformURI.

If that is the desired result for the original StandaloneSetup we just could change it accordingly.

Or, the present behavior should be mentioned in the class documentation. That might help other people to circumvent wondering why it doesn't work and try to find the root cause after two days.
Comment 5 Sven Efftinge CLA 2014-07-31 12:40:32 EDT
It indeed looks odd. It used to be like what you propose, but had been changed with this commit:

http://git.eclipse.org/c/emf/org.eclipse.mwe.git/commit/?id=5c9cfd46711bf2b820390334bc88e1bd89d4ecdd

I agree with Sebastian that it should stop looking into folders when it has found something. We could have a second 'setPlatformURIWithNestedProjects' that serves your use case, though.
But before we change this we should understand why that commit was done. :-)
Comment 6 Reiner Jung CLA 2014-08-04 04:13:19 EDT
I concur. Especially with the "before we change this we should understand why" part ;-)

For my personal "problem" I fixed it by creating a specialized class for my application. So there is no rush to change that again.