package net.enilink.komma.edit.ui.rcp.project; import java.io.BufferedInputStream; import java.io.InputStream; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.Queue; import java.util.Set; import java.util.WeakHashMap; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.QualifiedName; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Provides; import net.enilink.komma.common.adapter.IAdapterFactory; import net.enilink.komma.core.IGraph; import net.enilink.komma.core.IReference; import net.enilink.komma.core.IStatement; import net.enilink.komma.core.KommaException; import net.enilink.komma.core.KommaModule; import net.enilink.komma.core.LinkedHashGraph; import net.enilink.komma.core.URI; import net.enilink.komma.core.URIs; import net.enilink.komma.core.visitor.IDataVisitor; import net.enilink.komma.edit.KommaEditPlugin; import net.enilink.komma.edit.command.EditingDomainCommandStack; import net.enilink.komma.edit.command.IInputCallback; import net.enilink.komma.edit.domain.AdapterFactoryEditingDomain; import net.enilink.komma.edit.provider.ComposedAdapterFactory; import net.enilink.komma.edit.provider.ReflectiveItemProviderAdapterFactory; import net.enilink.komma.edit.ui.editor.InputCallbackDialog; import net.enilink.komma.edit.ui.rcp.KommaEditUIRCP; import net.enilink.komma.em.concepts.IClass; import net.enilink.komma.model.IModel; import net.enilink.komma.model.IModelSet; import net.enilink.komma.model.IModelSetFactory; import net.enilink.komma.model.IURIConverter; import net.enilink.komma.model.MODELS; import net.enilink.komma.model.ModelPlugin; import net.enilink.komma.model.ModelSetModule; import net.enilink.komma.model.ModelUtil; import net.enilink.komma.model.base.ExtensibleURIConverter; import net.enilink.komma.workbench.IProjectModelSet; import net.enilink.komma.workbench.ProjectModelSetSupport; import net.enilink.vocab.owl.OWL; import net.enilink.vocab.rdf.RDF; public class ProjectModelSetManager { protected static QualifiedName PROPERTY_MANAGER_INSTANCE = new QualifiedName(ProjectModelSetManager.class.getName(), "managerInstance"); protected static final String DEFAULT_CONFIG_FILE = ".komma"; public static ProjectModelSetManager getSharedInstance(IProject project) { ProjectModelSetManager manager = null; try { manager = (ProjectModelSetManager) project.getSessionProperty(PROPERTY_MANAGER_INSTANCE); } catch (CoreException e) { // ignore } if (manager == null) { manager = new ProjectModelSetManager(project); try { project.setSessionProperty(PROPERTY_MANAGER_INSTANCE, manager); } catch (CoreException e) { // ignore } } return manager; } protected ComposedAdapterFactory adapterFactory; protected final Set<Object> clients = Collections.newSetFromMap(new WeakHashMap<Object, Boolean>()); protected IModelSet modelSet; protected final IProject project; protected ProjectModelSetManager(IProject project) { this.project = project; } public void addClient(Object client) { clients.add(client); } public synchronized ComposedAdapterFactory getAdapterFactory() { if (adapterFactory == null) { // Create an adapter factory that yields item providers. adapterFactory = new ComposedAdapterFactory(ComposedAdapterFactory.IDescriptor.IRegistry.INSTANCE) { /** * Default adapter factory for all namespaces */ class DefaultItemProviderAdapterFactory extends ReflectiveItemProviderAdapterFactory { public DefaultItemProviderAdapterFactory() { super(KommaEditPlugin.getPlugin()); } @Override public Object adapt(Object object, Object type) { if (object instanceof IClass) { // do not override the adapter for classes return null; } return super.adapt(object, type); } @Override protected Collection<IClass> getTypes(Object object) { return object instanceof net.enilink.komma.em.concepts.IResource ? super.getTypes(object) : Collections.<IClass> emptyList(); } public boolean isFactoryForType(Object type) { // support any namespace return type instanceof URI || supportedTypes.contains(type); } } DefaultItemProviderAdapterFactory defaultAdapterFactory; { defaultAdapterFactory = new DefaultItemProviderAdapterFactory(); defaultAdapterFactory.setParentAdapterFactory(this); } @Override protected IAdapterFactory getDefaultAdapterFactory(Object type) { // provide a default adapter factory as fallback if no // specific adapter factory was found return defaultAdapterFactory; } @Inject protected void setInjector(Injector injector) { injector.injectMembers(defaultAdapterFactory); } }; createInjector().injectMembers(adapterFactory); } return adapterFactory; } protected IGraph getModelSetConfig(URI modelSetUri) { final IGraph config = new LinkedHashGraph(); final Queue<URI> toLoad = new LinkedList<>(); IResource configFile = project.findMember(DEFAULT_CONFIG_FILE); if (configFile != null) { toLoad.add(URIs.createPlatformResourceURI(configFile.getFullPath().toString(), true)); } if (!toLoad.isEmpty()) { Set<URI> seen = new HashSet<>(); IURIConverter uriConverter = new ExtensibleURIConverter(); while (!toLoad.isEmpty()) { URI uri = toLoad.remove(); if (seen.add(uri)) { try { String baseUri; String mimeType; if (DEFAULT_CONFIG_FILE.equals(uri.lastSegment())) { // special handling of configuration file baseUri = modelSetUri.toString(); mimeType = "text/turtle"; } else { // default handling of imported files baseUri = uri.toString(); mimeType = (String) uriConverter.contentDescription(uri, null) .get(IURIConverter.ATTRIBUTE_MIME_TYPE); } try (InputStream in = new BufferedInputStream(uriConverter.createInputStream(uri))) { ModelUtil.readData(in, baseUri, mimeType, new IDataVisitor<Void>() { @Override public Void visitBegin() { return null; } @Override public Void visitEnd() { return null; } @Override public Void visitStatement(IStatement stmt) { config.add(stmt); if (OWL.PROPERTY_IMPORTS.equals(stmt.getPredicate()) && stmt.getObject() instanceof IReference) { URI imported = ((IReference) stmt.getObject()).getURI(); if (imported != null) { toLoad.add(imported); } } return null; } }); } } catch (Exception e) { KommaEditUIRCP.getPlugin().log(new KommaException("Unable to read config file", e)); } } } } return config; } /** * Returns or creates a project-wide model set. * * The config for the model set is read from a Turtle RDF file named * '.komma' within the root folder or has default values (memory store with * RDFS inference). * * @return An instance of {@link IModelSet} */ public synchronized IModelSet getModelSet() { if (modelSet == null) { KommaModule module = ModelPlugin.createModelSetModule(getClass().getClassLoader()); module.addConcept(IProjectModelSet.class); module.addBehaviour(ProjectModelSetSupport.class); IModelSetFactory factory = Guice.createInjector(new ModelSetModule(module)) .getInstance(IModelSetFactory.class); URI modelSetUri = URIs.createURI("modelset:project:" + project.getName()); IGraph config = getModelSetConfig(modelSetUri); if (config.isEmpty()) { // create a default configuration config.add(modelSetUri, RDF.PROPERTY_TYPE, MODELS.TYPE_MODELSET); config.add(modelSetUri, RDF.PROPERTY_TYPE, MODELS.NAMESPACE_URI.appendLocalPart("MemoryModelSet")); config.add(modelSetUri, MODELS.NAMESPACE_URI.appendLocalPart("inference"), true); } config.add(modelSetUri, RDF.PROPERTY_TYPE, MODELS.NAMESPACE_URI.appendLocalPart("ProjectModelSet")); modelSet = factory.createModelSet(modelSetUri, config); if (modelSet instanceof IProjectModelSet && project != null) { ((IProjectModelSet) modelSet).setProject(project); } initializeEditingDomain(modelSet); } return modelSet; } protected Injector createInjector() { return Guice.createInjector(new AbstractModule() { @Override protected void configure() { bind(IInputCallback.class).to(InputCallbackDialog.class); } @Provides IAdapterFactory provideAdapterFactory() { return getAdapterFactory(); } }); } protected void initializeEditingDomain(IModelSet modelSet) { // Create the command stack that will notify this editor as commands // are executed. EditingDomainCommandStack commandStack = new EditingDomainCommandStack(); AdapterFactoryEditingDomain editingDomain = new AdapterFactoryEditingDomain(getAdapterFactory(), commandStack, modelSet); commandStack.setEditingDomain(editingDomain); editingDomain.setModelToReadOnlyMap(new java.util.WeakHashMap<IModel, Boolean>()); } public void removeClient(Object client) { clients.remove(client); if (clients.isEmpty()) { if (modelSet != null) { // dipose shared adapter factory if (adapterFactory != null) { adapterFactory.dispose(); adapterFactory = null; } modelSet.dispose(); modelSet = null; // remove shared properties from project try { project.setSessionProperty(PROPERTY_MANAGER_INSTANCE, null); } catch (CoreException e) { // ignore } } } } }