/*******************************************************************************
* Copyright (c) 2007, 2011 Willink Transformations and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* E.D.Willink - initial API and implementation
*******************************************************************************/
package org.eclipse.ocl.examples.modelregistry.model;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IResourceStatus;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.common.util.WrappedException;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
import org.eclipse.ocl.examples.modelregistry.ModelRegistration;
import org.eclipse.ocl.examples.modelregistry.ModelRegistry;
import org.eclipse.ocl.examples.modelregistry.ModelRegistryFactory;
import org.eclipse.ocl.examples.modelregistry.ModelRegistryPackage;
import org.eclipse.ocl.examples.modelregistry.ModelRegistrySettings;
import org.eclipse.ocl.examples.modelregistry.environment.FileHandle;
import org.eclipse.ocl.examples.modelregistry.environment.ProjectHandle;
import org.eclipse.ocl.examples.modelregistry.environment.ModelRegistryEnvironment;
import org.eclipse.ocl.examples.modelregistry.environment.ModelSerializationRegistry;
/**
* A ProjectRegistry instance forms the root of a model registry. It provides the in-memory
* form of the org.eclipse.ocl.modelregistry model read from and updated in the
* project .settings folder.
*
* The ProjectRegistry contains a FileHandleRegistry for each file handle for which models are
* registered. The FileHandleRegistry in turn contains an AccessorRegistry for each Accessor class
* and the AccessorRegistry contains the model Registrations.
*/
public class ProjectRegistry
{
public static final String DEFAULT_SERIALISATION_NAME = "XML";
private final ProjectHandle projectHandle;
private final Map<FileHandle, FileHandleRegistry> map = new HashMap<FileHandle, FileHandleRegistry>();
private final ResourceSet resourceSet;
private Resource model = null;
private Map<String, String> unregisteredNamespaces = null;
// FIXME Add a ResourceChangeListener to reload registry on external change.
public ProjectRegistry(ProjectHandle projectHandle) {
this.projectHandle = projectHandle;
resourceSet = new ResourceSetImpl();
resourceSet.getPackageRegistry().put(ModelRegistryPackage.eINSTANCE.getNsURI(), ModelRegistryPackage.eINSTANCE);
resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put("*", new XMIResourceFactoryImpl());
}
public FileHandleRegistry add(FileHandle fileHandle) {
FileHandleRegistry fileHandleRegistry = new FileHandleRegistry(this, fileHandle);
map.put(fileHandle, fileHandleRegistry);
return fileHandleRegistry;
}
public <A extends Accessor<A>> Registration<A> add(Registration<A> registration) {
FileHandle fileHandle = registration.getFileHandle();
FileHandleRegistry fileHandleRegistry = getOrCreate(fileHandle);
return fileHandleRegistry.add(registration);
}
private ModelRegistrySettings exportToModel() {
ModelRegistrySettings modelRegistrySettings = ModelRegistryFactory.eINSTANCE.createModelRegistrySettings();
List<FileHandleRegistry> fileHandleRegistries = new ArrayList<FileHandleRegistry>(map.values());
Collections.sort(fileHandleRegistries);
for (FileHandleRegistry fileHandleRegistry : fileHandleRegistries) {
FileHandle fileHandle = fileHandleRegistry.getFileHandle();
ModelRegistry modelRegistry = ModelRegistryFactory.eINSTANCE.createModelRegistry();
modelRegistry.setName(fileHandle.getProjectRelativeName());
modelRegistrySettings.getResource().add(modelRegistry);
List<AccessorRegistry<?>> accessorRegistries = new ArrayList<AccessorRegistry<?>>(fileHandleRegistry.getRegistries());
Collections.sort(accessorRegistries);
for (AccessorRegistry<?> accessorRegistry : accessorRegistries) {
List<Registration<?>> registrations = new ArrayList<Registration<?>>(accessorRegistry.getRegistrations());
Collections.sort(registrations);
for (Registration<?> registration : registrations) {
Accessor<?> accessor = registration.getAccessor();
ModelRegistration modelRegistration = ModelRegistryFactory.eINSTANCE.createModelRegistration();
modelRegistration.setAccessor(accessor.getName());
modelRegistration.setKind(accessor.getNamespace().getName());
if (!DEFAULT_SERIALISATION_NAME.equals(registration.getSerializationName()))
modelRegistration.setSerialization(registration.getSerializationName());
modelRegistration.setUri(registration.getURIString());
modelRegistry.getEntry().add(modelRegistration);
}
}
}
return modelRegistrySettings;
}
public FileHandleRegistry get(FileHandle fileHandle) {
return map.get(fileHandle);
}
public FileHandleRegistry getOrCreate(FileHandle fileHandle) {
FileHandleRegistry fileHandleRegistry = map.get(fileHandle);
if (fileHandleRegistry == null) {
fileHandleRegistry = new FileHandleRegistry(this, fileHandle);
map.put(fileHandle, fileHandleRegistry);
}
return fileHandleRegistry;
}
public ProjectHandle getProject() { return projectHandle; }
/**
* Return the registration applicable to accessor in the context of fileHandle
* @param <A> kind of accessor
* @param fileHandle defining look-up context
* @param accessor name of accessor
* @return the registration
*/
public <A extends Accessor<A>> Registration<A> getRegistration(FileHandle fileHandle, A accessor) {
for (FileHandle ancestorHandle = fileHandle; ancestorHandle != null; ancestorHandle = ancestorHandle.getParentFileHandle()) {
FileHandleRegistry fileHandleRegistry = get(ancestorHandle);
if (fileHandleRegistry != null) {
Registration<A> registration = fileHandleRegistry.getRegistration(accessor);
if (registration != null)
return registration;
}
}
return null;
}
/**
* Return all registrations applicable to accessorClass in the context of fileHandle, typically
* to treat all such registrations as a look-up path.
* @param fileHandle defining look-up context
* @param namespace name of accessor
* @return the registrations
*/
public <A extends Accessor<A>> Collection<Registration<A>> getRegistrations(FileHandle fileHandle, Accessor.Namespace<A> namespace) {
AccessorRegistry<A> flatAccessorRegistry = new AccessorRegistry<A>(get(fileHandle), namespace);
for (FileHandle ancestorHandle = fileHandle; ancestorHandle != null; ancestorHandle = ancestorHandle.getParentFileHandle()) {
FileHandleRegistry fileHandleRegistry = get(ancestorHandle);
if (fileHandleRegistry != null) {
AccessorRegistry<A> accessorRegistry = fileHandleRegistry.get(namespace);
if (accessorRegistry != null) {
for (Registration<A> registration : accessorRegistry.getRegistrations()) {
if (flatAccessorRegistry.get(registration.getAccessor()) == null)
flatAccessorRegistry.add(registration);
}
}
}
}
return flatAccessorRegistry.getRegistrations();
}
/**
* Return the URI that resolves accessor within the context of fileHandle
* after resolution against the URI of the project.
*
* @param fileHandle defining look-up context
* @param accessor name and kind of accessor
* @return the resolved URI
*/
public <A extends Accessor<A>> URI getResolvedURI(FileHandle fileHandle, A accessor) {
return resolveURI(getURI(fileHandle, accessor));
}
/**
* Return the ResourceSet that contains the model registry.
*/
public ResourceSet getResourceSet() { return resourceSet; }
/**
* Return the URI that resolves accessor within the context of fileHandle.
* @param fileHandle defining look-up context
* @param accessor name and kind of accessor
* @return the URI
*/
public <A extends Accessor<A>> URI getURI(FileHandle fileHandle, A accessor) {
Registration<?> registration = getRegistration(fileHandle, accessor);
return registration != null ? registration.getURI() : null;
}
private <A extends Accessor<A>> void importFromModel(Resource model, URI registryURI, Accessor.Namespace<A> unusedNamespace) {
// The unusedNamespace parameter avoids a spurious cannot bind A error.
map.clear();
NamespaceRegistry namespaceRegistry = ModelRegistryEnvironment.getInstance().getNamespaceRegistry();
ModelSerializationRegistry modelSerializationRegistry = ModelRegistryEnvironment.getInstance().getModelSerializationRegistry();
for (EObject content : model.getContents()) {
ModelRegistrySettings settings = (ModelRegistrySettings) content;
for (ModelRegistry modelRegistry : settings.getResource()) {
String name = modelRegistry.getName();
FileHandle fileHandle = null;
try {
fileHandle = projectHandle.getFileHandle(name);
} catch (IOException e) {
ModelRegistryEnvironment.logError("Illegal model registry file handle name", e);
}
if (fileHandle != null) {
FileHandleRegistry fileHandleRegistry = add(fileHandle);
for (ModelRegistration modelRegistration : modelRegistry.getEntry()) {
String namespaceName = modelRegistration.getKind();
A accessor = namespaceRegistry.createAccessor(namespaceName, modelRegistration.getAccessor());
if (accessor == null) {
if (unregisteredNamespaces == null)
unregisteredNamespaces = new HashMap<String,String>();
if (!unregisteredNamespaces.containsKey(namespaceName)) {
unregisteredNamespaces.put(namespaceName, namespaceName);
ModelRegistryEnvironment.logError("Unregistered accessor namespace '" + namespaceName + "' used in '" + registryURI + "'", null);
}
@SuppressWarnings("unchecked")
A castAccessor = (A) new UnregisteredAccessor(namespaceName, modelRegistration.getAccessor());
accessor = castAccessor;
}
URI ecoreURI = URI.createURI(modelRegistration.getUri(), false);
String serializationName = modelRegistration.getSerialization();
if (serializationName == null)
serializationName = DEFAULT_SERIALISATION_NAME;
ModelSerialization modelSerialisation = modelSerializationRegistry.getSerializationOrCreate(serializationName);
fileHandleRegistry.add(accessor, ecoreURI, modelSerialisation);
}
}
}
}
}
public boolean loadModel() {
List<URI> registryURIs = projectHandle.getRegistryURIs();
for (URI registryURI : registryURIs) {
try {
try {
if (model == null)
model = resourceSet.getResource(registryURI, true);
importFromModel(model, registryURI, ModelNameAccessor.NAMESPACE);
return true;
} catch (WrappedException e) {
throw e.exception();
}
} catch (FileNotFoundException e) {
; // May need to create model registry from scratch
} catch (CoreException e) {
IStatus status = e.getStatus();
if ((status.getCode() != IResourceStatus.RESOURCE_NOT_FOUND) || !status.getPlugin().equals(ResourcesPlugin.PI_RESOURCES)) {
ModelRegistryEnvironment.logError("Failed to get model registry from '" + registryURI + "'", e);
}
} catch (Exception e) {
ModelRegistryEnvironment.logError("Failed to get model registry from '" + registryURI + "'", e);
}
}
return false;
}
public <A extends Accessor<A>> Registration<A> remove(Registration<A> registration) {
FileHandle fileHandle = registration.getFileHandle();
FileHandleRegistry fileHandleRegistry = map.get(fileHandle);
return fileHandleRegistry.remove(registration);
}
/**
* Return the URI after resolution against the project file handle URI.
* @param uri uri to resolve
* @return the resolved URI
*/
public URI resolveURI(URI uri) {
if (uri == null)
return uri;
return uri.resolve(projectHandle.getURI());
}
public boolean saveModel() {
URI registryURI = projectHandle.getRegistryURI();
ModelRegistrySettings settings = exportToModel();
model = resourceSet.createResource(registryURI);
model.getContents().add(settings);
try {
model.save(null); // FIXME rename original as a backup till successful
projectHandle.refreshRegistry();
} catch (IOException e) {
ModelRegistryEnvironment.logError("Failed to create model registry at '" + registryURI + "'", e);
return false;
}
return true;
}
@Override public String toString() {
return getProject().toString();
}
}