blob: 8d27bdb25ca562ef305bf96498d49d1d11a4c16c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2016 Mia-Software.
* 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:
* Fabien Giquel (Mia-Software) - initial API and implementation
* Grégoire Dupé (Mia-Software) - Bug 491546 - [Discovery] Poor saving failure message
*******************************************************************************/
package org.eclipse.modisco.infra.discovery.core;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.Resource.IOWrappedException;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl;
import org.eclipse.gmt.modisco.infra.common.core.logging.MoDiscoLogger;
import org.eclipse.modisco.infra.discovery.core.annotations.Parameter;
import org.eclipse.modisco.infra.discovery.core.exception.DiscoveryException;
import org.eclipse.modisco.infra.discovery.core.internal.Activator;
/**
* A base implementation for a category of discoverers which handle some
* resulting model. It proposes to centralize some usual parameters :
*
* <ul>
* <li>
* A model container ({@link org.eclipse.emf.ecore.resource.Resource}), usually
* an output of the discovery but which might be provided also as input.
* <li>
* A boolean indicating if some serialization is wished.
* <li>
* A location for optional serialization (
* {@link org.eclipse.emf.common.util.URI})
* </ul>
*
* @param <T>
* the type of source of the discovery
*/
public abstract class AbstractModelDiscoverer<T> extends AbstractDiscoverer<T> {
private static final int FLUSH_LIMIT_SHIFT = 16;
private static final Integer FLUSH_LIMIT = Integer
.valueOf(1 << AbstractModelDiscoverer.FLUSH_LIMIT_SHIFT);
private URI fDefaultTargetURI = null;
private Resource fTargetModel;
@Parameter(name = "TARGET_MODEL", requiresInputValue = false, description = "A model container; usually an output of the discovery but may also be provided as input.")
public Resource getTargetModel() {
return this.fTargetModel;
}
@Parameter(name = "TARGET_MODEL")
public void setTargetModel(final Resource targetModel) {
this.fTargetModel = targetModel;
}
private boolean fSerializeTarget;
protected boolean isTargetSerializationChosen() {
return this.fSerializeTarget;
}
@Parameter(name = "SERIALIZE_TARGET", requiresInputValue = false, description = "Whether to serialize the target model. The save behavior depends on the Discoverer implementation. The standard behavior is to use XMI serialization.")
public void setSerializeTarget(final boolean serializeTarget) {
this.fSerializeTarget = serializeTarget;
}
private URI fTargetURI;
public URI getTargetURI() {
return this.fTargetURI;
}
@Parameter(name = "TARGET_URI", requiresInputValue = false, description = "A target location for optional model serialization. Save behavior depends on the Discoverer implementation.")
public void setTargetURI(final URI targetURI) {
this.fTargetURI = targetURI;
}
private ResourceSet fResourceSet = null;
protected ResourceSet getResourceSet() {
if (this.fResourceSet == null) {
this.fResourceSet = new ResourceSetImpl();
}
return this.fResourceSet;
}
protected void setResourceSet(final ResourceSet resourceSet) {
this.fResourceSet = resourceSet;
}
/**
* Creates and returns the target model of the discovery. If the target
* model has already been created or it was provided through the
* TARGET_MODEL discoverer parameter, then it is returned instead of
* creating a new one.
* <p>
* The discoverer developer should explicitly call it when implementing
* their discoverer.
*
* @return the target model
*/
protected Resource createTargetModel() {
Resource targetModel = getTargetModel();
if (targetModel != null) {
return targetModel;
}
targetModel = new XMIResourceImpl();
getResourceSet().getResources().add(targetModel);
setTargetModel(targetModel);
return targetModel;
}
/**
* Utility service proposing usual target model serialization. The
* discoverer developer may explicitly call it when implementing their
* discoverer.
* <p>
* It may be overridden to specify a different way of serializing the
* Resource.
*
* @throws IOException
* exception on serialization
*/
protected void saveTargetModel() throws IOException {
if (getTargetModel() != null) {
Map<String, Object> options = new HashMap<String, Object>();
options.put(XMLResource.OPTION_FLUSH_THRESHOLD, AbstractModelDiscoverer.FLUSH_LIMIT);
options.put(XMLResource.OPTION_USE_FILE_BUFFER, Boolean.TRUE);
final URI targetURI = getTargetURI();
URI defaultURI = getDefaultTargetURI();
final Resource targetModel = getTargetModel();
if (targetURI != null && targetURI.toString().trim().length() > 0) {
targetModel.setURI(targetURI);
} else if (defaultURI != null) {
getTargetModel().setURI(defaultURI);
} else if (targetModel.getURI() == null) {
MoDiscoLogger.logError("Cannot save a Resource without URI. " //$NON-NLS-1$
+ "You must pass a valid URI using " //$NON-NLS-1$
+ "AbstractModelDiscoverer#setTargetURI", Activator.getDefault()); //$NON-NLS-1$
return;
}
try {
targetModel.save(options);
} catch (IOWrappedException e) {
// TODO here is a temporary fix for Bug 323247
if (e.getCause() != null && (e.getCause() instanceof CoreException)) {
MoDiscoLogger.logWarning(e,
"XMI serialization has failed on '" + targetModel.getURI() //$NON-NLS-1$
+ "'. Trying again without File Buffer.", //$NON-NLS-1$
Activator.getDefault());
options.clear();
targetModel.save(options);
MoDiscoLogger.logInfo("XMI serialization is done for " + targetModel.getURI(), //$NON-NLS-1$
Activator.getDefault());
} else {
throw e;
}
}
}
}
/**
* Discovers the model and saves the result model if
* {@link AbstractModelDiscoverer#isTargetSerializationChosen()
* serialization is required}.
*/
@Override
public final void discoverElement(final T source, final IProgressMonitor monitor)
throws DiscoveryException {
super.discoverElement(source, monitor);
if (monitor.isCanceled()) {
return;
}
monitor.subTask(Messages.AbstractModelDiscoverer_savingModel);
if (isTargetSerializationChosen()) {
try {
saveTargetModel();
} catch (Exception e) {
final String message = String.format(
"The saving step of '%s' failed (targetURI='%s')", //$NON-NLS-1$
this.getClass().getName(),
this.getTargetURI());
MoDiscoLogger.logError(e, message, Activator.getDefault());
}
}
}
/**
* Set the URI that must be used if the targetURI is <code>null</code> or
* empty.
*/
protected void setDefaultTargetURI(final URI defaultURI) {
this.fDefaultTargetURI = defaultURI;
}
/**
* The URI that will be used if the targetURI is <code>null</code> or empty.
*/
public URI getDefaultTargetURI() {
return this.fDefaultTargetURI;
}
}