package org.xpect.registry;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.Resource.Factory;
import org.eclipse.xtext.resource.IResourceFactory;
import org.eclipse.xtext.resource.IResourceServiceProvider;
import org.eclipse.xtext.util.Strings;
import org.xpect.registry.IEmfFileExtensionInfo.IXtextFileExtensionInfo;
import com.google.common.base.Joiner;
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.inject.Module;
public class FileExtensionInfoRegistry implements IEmfFileExtensionInfo.Registry {
private abstract static class Data {
protected LazyClass<Object> editor = null;
protected String languageID = null;
protected LazyClass<Factory> resFact = null;
protected LazyClass<IResourceServiceProvider> resSvP = null;
protected LazyClass<IResourceServiceProvider> resUISvP = null;
protected LazyClass<Module> runtimeModule = null;
protected LazyClass<Module> sharedModule = null;
protected LazyClass<Module> uiModule = null;
abstract protected String getFileExtensionString();
@Override
public String toString() {
ToStringHelper result = Objects.toStringHelper(this).add("fileExtension", getFileExtensionString());
if (resFact != null && !DEFAULT_RESOURCE_FACTORY.equals(resFact.getName()))
result.add("resourceFactory", resFact);
if (languageID != null)
result.add("languageID", languageID);
if (runtimeModule != null)
result.add("runtimeModule", runtimeModule);
if (uiModule != null)
result.add("uiModule", uiModule);
if (sharedModule != null && !DEFAULT_SHARED_STATE_MODULE.equals(sharedModule.getName()))
result.add("sharedStateModule", sharedModule);
if (resSvP != null && !DEFAULT_RESOURCE_SERVICE_PROVIDER.equals(resSvP.getName()))
result.add("resourceServiceProvider", resSvP);
if (resUISvP != null && !DEFAULT_RESOURCE_UI_SERVICE_PROVIDER.equals(resUISvP.getName()))
result.add("resourceUIServiceProvider", resUISvP);
return result.toString();
}
}
public static class EmfFileExtensionInfo implements IEmfFileExtensionInfo {
private final Set<String> fileExtensions;
private final LazyClass<Factory> resourceFactory;
private final Set<IExtensionInfo> traces;
public EmfFileExtensionInfo(Set<String> fileExtensions, LazyClass<Factory> resourceFactory, Set<IExtensionInfo> traces) {
super();
this.traces = ImmutableSet.copyOf(traces);
this.fileExtensions = ImmutableSet.copyOf(fileExtensions);
this.resourceFactory = resourceFactory;
}
public Set<String> getFileExtensions() {
return fileExtensions;
}
public LazyClass<Factory> getResourceFactory() {
return resourceFactory;
}
public Set<IExtensionInfo> getTraces() {
return traces;
}
@Override
public String toString() {
List<String> extensions = Lists.newArrayList(fileExtensions);
Collections.sort(extensions);
ToStringHelper result = Objects.toStringHelper(IEmfFileExtensionInfo.class).add("fileExtensions", extensions);
if (resourceFactory != null && !DEFAULT_RESOURCE_FACTORY.equals(resourceFactory.getName()))
result.add("resourceFactory", resourceFactory);
return result.toString();
}
}
private static class ExtensionPointData extends Data {
private Set<String> fileExtensions;
private IExtensionInfo trace;
public ExtensionPointData(IExtensionInfo trace, String fileExtensionAttributeName) {
this.trace = trace;
this.fileExtensions = Sets.newHashSet();
String exts = trace.getAttributeValue(fileExtensionAttributeName);
if (exts != null)
for (String ext : exts.split(","))
this.fileExtensions.add(ext.trim());
}
@Override
protected String getFileExtensionString() {
return Joiner.on(", ").join(fileExtensions);
}
}
private static class FileExtensionData extends Data {
protected String fileExtension;
protected Set<IExtensionInfo> traces;
@Override
protected String getFileExtensionString() {
return fileExtension;
}
public List<String> getLocations() {
List<String> locations = Lists.newArrayList();
for (IExtensionInfo trace : traces) {
String location = trace.getLocation();
if (!Strings.isEmpty(location))
locations.add(location);
}
return locations;
}
}
public static class XtextFileExtensionInfo extends EmfFileExtensionInfo implements IXtextFileExtensionInfo {
private final String languageID;
private final LazyClass<IResourceServiceProvider> resourceServiceProvider;
private final LazyClass<IResourceServiceProvider> resourceUIServiceProvider;
private final LazyClass<Module> runtimeModule;
private final LazyClass<Module> sharedModule;
private final LazyClass<Module> uiModule;
public XtextFileExtensionInfo(Set<String> fileExtensions, LazyClass<Factory> resourceFactory, String languageID,
LazyClass<IResourceServiceProvider> resourceServiceProvider, LazyClass<IResourceServiceProvider> resourceUIServiceProvider, LazyClass<Module> runtimeModule,
LazyClass<Module> uiModule, LazyClass<Module> sharedModule, Set<IExtensionInfo> traces) {
super(fileExtensions, resourceFactory, traces);
this.languageID = languageID;
this.resourceServiceProvider = resourceServiceProvider;
this.resourceUIServiceProvider = resourceUIServiceProvider;
this.runtimeModule = runtimeModule;
this.uiModule = uiModule;
this.sharedModule = sharedModule;
}
public String getLanguageID() {
return languageID;
}
public LazyClass<IResourceServiceProvider> getResourceServiceProvider() {
return resourceServiceProvider;
}
public LazyClass<IResourceServiceProvider> getResourceUIServiceProvider() {
return resourceUIServiceProvider;
}
public LazyClass<Module> getRuntimeModule() {
return runtimeModule;
}
public LazyClass<Module> getSharedModule() {
return sharedModule;
}
public LazyClass<Module> getUIModule() {
return uiModule;
}
public boolean hasInjector() {
return runtimeModule != null;
}
@Override
public String toString() {
List<String> extensions = Lists.newArrayList(getFileExtensions());
Collections.sort(extensions);
ToStringHelper result = Objects.toStringHelper(IXtextFileExtensionInfo.class).add("fileExtensions", extensions);
if (getResourceFactory() != null && !DEFAULT_RESOURCE_FACTORY.equals(getResourceFactory().getName()))
result.add("resourceFactory", getResourceFactory());
if (languageID != null)
result.add("languageID", languageID);
if (runtimeModule != null)
result.add("runtimeModule", runtimeModule);
if (uiModule != null)
result.add("uiModule", uiModule);
if (resourceServiceProvider != null && !DEFAULT_RESOURCE_SERVICE_PROVIDER.equals(resourceServiceProvider.getName()))
result.add("resourceServiceProvider", resourceServiceProvider);
if (resourceUIServiceProvider != null && !DEFAULT_RESOURCE_UI_SERVICE_PROVIDER.equals(resourceUIServiceProvider.getName()))
result.add("resourceUIServiceProvider", resourceUIServiceProvider);
return result.toString();
}
}
private static final String DEFAULT_RESOURCE_FACTORY = IResourceFactory.class.getName();
private static final String DEFAULT_RESOURCE_SERVICE_PROVIDER = IResourceServiceProvider.class.getName();
private static final String DEFAULT_RESOURCE_UI_SERVICE_PROVIDER = "org.eclipse.xtext.ui.resource.IResourceUIServiceProvider";
private static final String DEFAULT_SHARED_STATE_MODULE = "org.eclipse.xtext.ui.shared.SharedStateModule";
public static void main(String[] args) {
System.out.println(new FileExtensionInfoRegistry());
}
private final Map<String, IEmfFileExtensionInfo> fileExtension2Info;
private final List<IEmfFileExtensionInfo> infos;
private final List<String> issues;
public FileExtensionInfoRegistry() {
this(IExtensionInfo.Registry.INSTANCE);
}
public FileExtensionInfoRegistry(IExtensionInfo.Registry registry) {
List<String> issues = Lists.newArrayList();
this.infos = ImmutableList.copyOf(collectFileExtensionInfos(registry, issues));
this.fileExtension2Info = ImmutableMap.copyOf(collectFileExtensionInfosByExt(infos));
this.issues = ImmutableList.copyOf(issues);
}
private List<IEmfFileExtensionInfo> collectFileExtensionInfos(IExtensionInfo.Registry registry, Collection<String> issues) {
List<ExtensionPointData> infos = Lists.newArrayList();
for (IExtensionInfo ext : registry.getExtensions("org.eclipse.emf.ecore.extension_parser"))
infos.add(parseEmfExtensionParser(ext));
for (IExtensionInfo ext : registry.getExtensions("org.eclipse.xtext.extension_resourceServiceProvider"))
infos.add(parseXtextResourceUIServiceProvider(ext));
for (IExtensionInfo ext : registry.getExtensions("org.eclipse.ui.editors")) {
ExtensionPointData editorInfo = parseEditorExtension(ext);
if (editorInfo.editor != null && editorInfo.editor.getFactory() != null)
infos.add(editorInfo);
}
for (IExtensionInfo ext : registry.getExtensions("org.xpect.fileExtensions"))
infos.add(parseXpectFileExtensionInfo(ext));
Multimap<String, ExtensionPointData> ext2info = HashMultimap.create();
for (ExtensionPointData info : infos)
for (String ext : info.fileExtensions)
if (ext != null && !"___xbase".equals(ext) && !"xt".equals(ext))
ext2info.put(ext, info);
List<IEmfFileExtensionInfo> allInfos = Lists.newArrayList();
Multimap<String, FileExtensionData> name2xtextInfo = HashMultimap.create();
for (String ext : ext2info.keySet()) {
FileExtensionData merged = mergeByFileExt(ext, ext2info.get(ext), issues);
if (merged.languageID == null)
allInfos.add(toEmfFileExtensionInfo(merged, issues));
else
name2xtextInfo.put(merged.languageID, merged);
}
for (String name : name2xtextInfo.keySet())
allInfos.add(mergeByLang(name, name2xtextInfo.get(name), issues));
return allInfos;
}
private Map<String, IEmfFileExtensionInfo> collectFileExtensionInfosByExt(List<IEmfFileExtensionInfo> allInfos) {
Map<String, IEmfFileExtensionInfo> result = Maps.newHashMap();
for (IEmfFileExtensionInfo info : allInfos)
for (String ext : info.getFileExtensions())
result.put(ext, info);
return result;
}
public IEmfFileExtensionInfo getEmfFileExtensionInfo(String fileExtension) {
return fileExtension2Info.get(fileExtension);
}
public Collection<IEmfFileExtensionInfo> getFileExtensionInfos() {
return infos;
}
private <T> T merge(T o1, T o2, Collection<IExtensionInfo> traceIn, Set<IExtensionInfo> tracOut, String name, String context, Collection<String> issues) {
if (o2 != null) {
Collection<IExtensionInfo> trace;
if (o2 instanceof LazyClass<?>) {
IExtensionInfo cand = ((LazyClass<?>) o2).getTrace();
trace = cand == null ? Collections.<IExtensionInfo> emptySet() : Collections.singleton(cand);
} else
trace = traceIn;
if (o1 == null) {
if (trace != null)
tracOut.addAll(trace);
return o2;
} else if (name != null && !o1.equals(o2)) {
String message = "Conflicting " + name + ": '" + o1 + "' and '" + o2 + "'";
HashSet<IExtensionInfo> set = Sets.newHashSet(tracOut);
set.addAll(trace);
List<String> tr = Lists.newArrayList();
for (IExtensionInfo e : set)
tr.add(e + " from " + e.getLocation());
issues.add("WARNING:" + message + " " + context + " Traces:\n\t" + Joiner.on("\n\t").join(tr));
}
}
return o1;
}
private FileExtensionData mergeByFileExt(String fileExtension, Collection<ExtensionPointData> infos, Collection<String> issues) {
FileExtensionData result = new FileExtensionData();
result.fileExtension = fileExtension;
result.traces = Sets.newHashSet();
String context = "FileExtension: " + fileExtension;
for (ExtensionPointData info : infos) {
Collection<IExtensionInfo> trace = Collections.singleton(info.trace);
result.languageID = merge(result.languageID, info.languageID, trace, result.traces, "Xtext LanguageIDs", context, issues);
result.resFact = merge(result.resFact, info.resFact, trace, result.traces, "EMF Resource Factories", context, issues);
result.resSvP = merge(result.resSvP, info.resSvP, trace, result.traces, "Xtext Resource Service Provider", context, issues);
result.resUISvP = merge(result.resUISvP, info.resUISvP, trace, result.traces, "Xtext Resource UI Service Provider", context, issues);
result.runtimeModule = merge(result.runtimeModule, info.runtimeModule, trace, result.traces, "Xtext Runtime Modules", context, issues);
result.uiModule = merge(result.uiModule, info.uiModule, trace, result.traces, "Xtext UI Modules", context, issues);
result.sharedModule = merge(result.sharedModule, info.sharedModule, trace, result.traces, "Xtext Shared State Modules", context, issues);
result.editor = merge(result.editor, info.editor, trace, result.traces, null, context, issues);
}
return result;
}
private XtextFileExtensionInfo mergeByLang(String langID, Collection<FileExtensionData> infos, Collection<String> issues) {
Set<IExtensionInfo> traces = Sets.newHashSet();
Set<String> fileExtensions = Sets.newHashSet();
LazyClass<Factory> resFact = null;
LazyClass<IResourceServiceProvider> rsp = null;
LazyClass<IResourceServiceProvider> rUIsp = null;
LazyClass<Module> runtimeModule = null;
LazyClass<Module> uiModule = null;
LazyClass<Module> sharedModule = null;
LazyClass<Object> editor = null;
String context = "XtextLanguageID: " + langID;
for (FileExtensionData info : infos) {
fileExtensions.add(info.fileExtension);
resFact = merge(resFact, info.resFact, info.traces, traces, "EMF Resource Factories", context, issues);
rsp = merge(rsp, info.resSvP, info.traces, traces, "Xtext Resource Service Provider", context, issues);
rUIsp = merge(rUIsp, info.resUISvP, info.traces, traces, "Xtext Resource UI Service Provider", context, issues);
runtimeModule = merge(runtimeModule, info.runtimeModule, info.traces, traces, "Xtext Runtime Modules", context, issues);
uiModule = merge(uiModule, info.uiModule, info.traces, traces, "Xtext UI Modules", context, issues);
sharedModule = merge(sharedModule, info.sharedModule, info.traces, traces, "Xtext Shared State Modules", context, issues);
editor = merge(editor, info.editor, info.traces, traces, null, context, issues);
}
if (resFact == null && runtimeModule != null)
resFact = LazyClass.create(Resource.Factory.class, DEFAULT_RESOURCE_FACTORY, runtimeModule.getLoader());
if (rsp == null && runtimeModule != null)
rsp = LazyClass.create(IResourceServiceProvider.class, DEFAULT_RESOURCE_SERVICE_PROVIDER, runtimeModule.getLoader());
if (uiModule == null)
for (LazyClass<?> clazz : new LazyClass[] { rUIsp, editor, resFact, rsp })
if (clazz != null && clazz.getFactory() != null) {
uiModule = LazyClass.create(Module.class, clazz.getFactory() + "UiModule", clazz.getLoader());
break;
}
if (rUIsp == null && uiModule != null)
rUIsp = LazyClass.create(IResourceServiceProvider.class, DEFAULT_RESOURCE_UI_SERVICE_PROVIDER, uiModule.getLoader());
if (sharedModule == null && uiModule != null)
sharedModule = LazyClass.create(Module.class, DEFAULT_SHARED_STATE_MODULE, uiModule.getLoader());
return new XtextFileExtensionInfo(fileExtensions, resFact, langID, rsp, rUIsp, runtimeModule, uiModule, sharedModule, traces);
}
private ExtensionPointData parseEditorExtension(IExtensionInfo info) {
ExtensionPointData result = new ExtensionPointData(info, "extensions");
result.languageID = info.getAttributeValue("id");
result.runtimeModule = LazyClass.create(Module.class, result.languageID + "RuntimeModule", info);
result.editor = LazyClass.create(Object.class, info, "class");
return result;
}
private ExtensionPointData parseEmfExtensionParser(IExtensionInfo info) {
ExtensionPointData result = new ExtensionPointData(info, "type");
result.resFact = LazyClass.create(Factory.class, info, "class");
return result;
}
private ExtensionPointData parseXpectFileExtensionInfo(IExtensionInfo info) {
ExtensionPointData result = new ExtensionPointData(info, "fileExtension");
result.resFact = LazyClass.create(Resource.Factory.class, info, "emfResourceFactory");
result.languageID = info.getAttributeValue("xtextLanguageName");
result.resSvP = LazyClass.create(IResourceServiceProvider.class, info, "xtextResourceServiceProvider");
result.resUISvP = LazyClass.create(IResourceServiceProvider.class, info, "xtextResourceUIServiceProvider");
result.runtimeModule = LazyClass.create(Module.class, info, "xtextRuntimeModule");
result.uiModule = LazyClass.create(Module.class, info, "xtextUiModule");
result.sharedModule = LazyClass.create(Module.class, info, "xtextSharedStateModule");
return result;
}
private ExtensionPointData parseXtextResourceUIServiceProvider(IExtensionInfo info) {
ExtensionPointData result = new ExtensionPointData(info, "uriExtension");
result.resUISvP = LazyClass.create(IResourceServiceProvider.class, info, "class");
return result;
}
private EmfFileExtensionInfo toEmfFileExtensionInfo(FileExtensionData info, Collection<String> issues) {
warnIfSet(info, info.resSvP, "resourceServiceProvider", issues);
warnIfSet(info, info.resUISvP, "resourceUIServiceProvider", issues);
warnIfSet(info, info.runtimeModule, "runtimeModule", issues);
// warnIfSet(info, info.uiModule, "uiModule");
return new EmfFileExtensionInfo(Collections.singleton(info.fileExtension), info.resFact, info.traces);
}
@Override
public String toString() {
return Joiner.on("\n").join(Iterables.concat(issues, infos));
}
private void warnIfSet(FileExtensionData info, Object value, String name, Collection<String> issues) {
if (value != null)
issues.add("WARNING: Ignoring " + name + " '" + value + "' for fileExtension '" + info.fileExtension + "' from " + info.getLocations());
}
}