blob: 8339fb155acbc4bfe458e00f09583bc54cf78a6a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 ALL4TEC & CEA LIST.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* ALL4TEC & CEA LIST - initial API and implementation
******************************************************************************/
package org.polarsys.esf.core.common.adapter;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EContentAdapter;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.polarsys.esf.core.common.CommonActivator;
import org.polarsys.esf.core.common.listener.IProblemIndicationListener;
/**
* Content adapter used to analyse resources content and build the problem indication.
* It maintains a set of listeners to warn when a new diagnostic is built.
*
* NB : It extends {@link EContentAdapter} to listen to all the changes on a resource and its content.
*
* @author $Author: jdumont $
* @version $Revision: 83 $
*/
public class ProblemIndicationAdapter extends EContentAdapter {
/** Map to store the diagnostic associated with a resource. */
private Map<Resource, Diagnostic> mResourceToDiagnosticMap = new LinkedHashMap<Resource, Diagnostic>();
/** Set of listening objects, waiting for the diagnostics results. */
private Set<IProblemIndicationListener> mListenersSet = new HashSet<IProblemIndicationListener>();
/**
* Default constructor.
*/
public ProblemIndicationAdapter() {
// Nothing to do
}
/**
* {@inheritDoc}
*/
@Override
public void notifyChanged(final Notification pNotification) {
// Check if the notification come from a Resource
if (pNotification.getNotifier() instanceof Resource) {
// Switch according to the properties modified, the resource can
// be loading, or errors/warning can be identified
switch (pNotification.getFeatureID(Resource.class)) {
case Resource.RESOURCE__IS_LOADED:
case Resource.RESOURCE__ERRORS:
case Resource.RESOURCE__WARNINGS:
// Get the resource underlying the notification
Resource vResource = (Resource) pNotification.getNotifier();
// Analyse the resource and get its diagnostic
Diagnostic vDiagnostic = analyzeResourceProblems(vResource, null);
// If any error found, store it in the diagnostic map
// otherwise clean the potential existing diagnostic associated
// to this resource
if (vDiagnostic.getSeverity() != Diagnostic.OK) {
mResourceToDiagnosticMap.put(vResource, vDiagnostic);
} else {
mResourceToDiagnosticMap.remove(vResource);
}
// Warn all the listeners that the diagnostics has changed
fireProblemIndication();
break;
default:
// By default, nothing to do
break;
}
} else {
// Call the parent method for all the others type of notifiers
super.notifyChanged(pNotification);
}
}
/**
* {@inheritDoc}
*/
@Override
protected void setTarget(final Resource pTargetResource) {
// Set the resource to listen
basicSetTarget(pTargetResource);
}
/**
* {@inheritDoc}
*/
@Override
protected void unsetTarget(final Resource pTargetResource) {
// Unset the resource to listen
basicUnsetTarget(pTargetResource);
// Clean all the diagnostics linked to the previously listened resource
mResourceToDiagnosticMap.remove(pTargetResource);
// Warn all the listeners that the diagnostics has changed
fireProblemIndication();
}
/**
* Returns a diagnostic describing the errors and warnings listed in the resource
* and the specified exception (if any).
*
* @param pResource The resource to analyse
* @param pException The potential exception thrown during the resource creation or load. May be null
* @return The resulting diagnostic
*/
private Diagnostic analyzeResourceProblems(final Resource pResource, final Exception pException) {
// Create the default diagnostic to return
Diagnostic vDiagnostic = Diagnostic.OK_INSTANCE;
if (pResource == null) {
// The resource is null, create a basic error diagnostic
vDiagnostic = new BasicDiagnostic(
Diagnostic.ERROR,
CommonActivator.getPlugin().getSymbolicName(),
0,
CommonActivator.getMessages().getString(
"ProblemIndicationAdapter.diagnostics.error.null"), //$NON-NLS-1$
new Object[] {pException });
} else if (!pResource.getErrors().isEmpty() || !pResource.getWarnings().isEmpty()) {
// Build the diagnostic data with the given exception, or the resource directly
Object[] vDataArray = null;
if (pException == null) {
vDataArray = new Object[] {pException };
} else {
vDataArray = new Object[] {pResource };
}
// The resource is invalid, create a basic error diagnostic
vDiagnostic = new BasicDiagnostic(
Diagnostic.ERROR,
CommonActivator.getPlugin().getSymbolicName(),
0,
CommonActivator.getMessages().getString(
"ProblemIndicationAdapter.diagnostics.error.creation", //$NON-NLS-1$
new Object[] {pResource.getURI()}),
vDataArray);
// Add the details in the root diagnostic
((BasicDiagnostic) vDiagnostic).merge(EcoreUtil.computeDiagnostic(pResource, true));
} else if (pException != null) {
// An exception is given, create a basic diagnostic for it
vDiagnostic = new BasicDiagnostic(
Diagnostic.ERROR,
CommonActivator.getPlugin().getSymbolicName(),
0,
CommonActivator.getMessages().getString(
"ProblemIndicationAdapter.diagnostics.error.exception", //$NON-NLS-1$
new Object[] {pResource.getURI()}),
new Object[] {pException });
}
return vDiagnostic;
}
/**
* Analyse a resource, given in parameter, to identify its potential problems.
* All the errors or warnings found are saved to be able to build a global diagnostic
* and transfer it to the listeners at the end of this method execution.
*
* An exception can be given, to be integrated to the diagnostics, for example if it has
* been thrown during the resource load or save.
*
* @param pResource The resource to analyse
* @param pException The potential exception thrown during the resource creation or load. May be null
*/
public void analyzeResource(final Resource pResource, final Exception pException) {
boolean vDiagnosticsChanged = false;
// If the resource is not already registered with a problem,
// do its analyse, potentially with the given exception, and
// remember of the resulting diagnostic
if (!mResourceToDiagnosticMap.containsKey(pResource)) {
// Analyse the resource and get the resulting diagnostic
Diagnostic vDiagnostic = analyzeResourceProblems(pResource, pException);
// If an error is identified, register it in the map of diagnostic, for this resource
if (vDiagnostic.getSeverity() != Diagnostic.OK) {
mResourceToDiagnosticMap.put(pResource, vDiagnostic);
// Remember that the diagnostics have changed
vDiagnosticsChanged = true;
}
}
// Once the analysis is done, warn the listener if the diagnostics changed
if (vDiagnosticsChanged) {
fireProblemIndication();
}
}
/**
* Return a root diagnostic containing as children all the diagnostics
* for each analysed resource.
*
* The diagnostics are taken from the current state, resulting of the analysis
* previously done by this adapter.
*
* @return The current diagnostic
*/
public Diagnostic getDiagnostic() {
// Create the root diagnostic to enrich with the identified diagnostics
BasicDiagnostic vRootDiagnostic = new BasicDiagnostic(
Diagnostic.OK,
CommonActivator.getPlugin().getSymbolicName(),
0,
null,
null);
// Loop on the diagnostics identified, and add them to the root
// if they correspond to an error or a warning
for (Diagnostic vChildDiagnostic : mResourceToDiagnosticMap.values()) {
if (vChildDiagnostic.getSeverity() != Diagnostic.OK) {
vRootDiagnostic.add(vChildDiagnostic);
}
}
return vRootDiagnostic;
}
/**
* Add a listener for the diagnostics results.
* This has no effect if an identical listener is already registered.
*
* @param pListener The new listener to add
*/
public void addProblemIndicationListener(final IProblemIndicationListener pListener) {
mListenersSet.add(pListener);
}
/**
* Remove the given listener.
* This has no affect if the listener is not registered.
*
* @param pListener The listener to remove
*/
public void removeProblemIndicationListener(final IProblemIndicationListener pListener) {
mListenersSet.remove(pListener);
}
/**
* Warn all the listeners that a new diagnostic has been built.
*/
private void fireProblemIndication() {
// Get the new root diagnostic
Diagnostic vDiagnostic = getDiagnostic();
// Warn all the listeners
for (IProblemIndicationListener vListener : mListenersSet) {
vListener.updateProblemIndication(vDiagnostic);
}
}
}