blob: 355f90050b62b235b0761e7b718f3523943f323a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 Chalmers | University of Gothenburg, rt-labs 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:
* Chalmers | University of Gothenburg and rt-labs - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.capra.handler.php.notification;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.capra.core.adapters.ArtifactMetaModelAdapter;
import org.eclipse.capra.core.adapters.TracePersistenceAdapter;
import org.eclipse.capra.core.helpers.ExtensionPointHelper;
import org.eclipse.capra.handler.php.PhpHandler;
import org.eclipse.capra.ui.notification.CapraNotificationHelper;
import org.eclipse.capra.ui.notification.CapraNotificationHelper.IssueType;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.dltk.core.DLTKCore;
import org.eclipse.dltk.core.ElementChangedEvent;
import org.eclipse.dltk.core.IElementChangedListener;
import org.eclipse.dltk.core.IModelElement;
import org.eclipse.dltk.core.IModelElementDelta;
import org.eclipse.dltk.core.ISourceModule;
import org.eclipse.dltk.core.ISourceReference;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
/**
* Checks for changes of PHP Elements to determine if they affect the trace
* model. Creates markers on the artifact model if the changes affect artifact
* wrappers.
*
* @author Dusan Kalanj, Salome Maro
*/
public class PHPElementChangeListener implements IElementChangedListener {
ArtifactMetaModelAdapter artifactAdapter = ExtensionPointHelper.getArtifactWrapperMetaModelAdapter().get();
@Override
public void elementChanged(ElementChangedEvent event) {
TracePersistenceAdapter tracePersistenceAdapter = ExtensionPointHelper.getTracePersistenceAdapter().get();
EObject artifactModel = tracePersistenceAdapter.getArtifactWrappers(new ResourceSetImpl());
// get all artifacts
List<EObject> allArtifacts = artifactAdapter.getAllArtifacts(artifactModel);
List<EObject> phpArtifacts = allArtifacts.stream()
.filter(p -> artifactAdapter.getArtifactHandler(p).equals(PhpHandler.class.getName()))
.collect(Collectors.toList());
if (phpArtifacts.size() == 0)
return;
IPath path = new Path(EcoreUtil.getURI(artifactModel).toPlatformString(false));
IFile wrapperContainer = ResourcesPlugin.getWorkspace().getRoot().getFile(path);
new WorkspaceJob(CapraNotificationHelper.NOTIFICATION_JOB) {
@Override
public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException {
handleDelta(event.getDelta(), phpArtifacts, wrapperContainer);
return Status.OK_STATUS;
}
}.schedule();
}
private void handleDelta(IModelElementDelta delta, List<EObject> phpArtifacts, IFile wrapperContainer) {
if (!(delta.getElement() instanceof ISourceModule))
// Only go as far as the source file
for (IModelElementDelta subDelta : delta.getAffectedChildren())
handleDelta(subDelta, phpArtifacts, wrapperContainer);
int flags = delta.getFlags();
int changeType = delta.getKind();
IssueType issueType = null;
if (changeType == IModelElementDelta.ADDED)
issueType = IssueType.ADDED;
else if (changeType == IModelElementDelta.REMOVED) {
if ((flags & IModelElementDelta.F_MOVED_TO) != 0)
if (delta.getMovedToElement().getElementName().equals(delta.getElement().getElementName()))
issueType = IssueType.MOVED;
else
issueType = IssueType.RENAMED;
else
issueType = IssueType.DELETED;
} else if (changeType == IModelElementDelta.CHANGED && (flags & IModelElementDelta.F_PRIMARY_RESOURCE) != 0)
// This is only true if a source file changes.
issueType = IssueType.CHANGED;
if (issueType != null) {
String affectedElementUri = delta.getElement().getHandleIdentifier();
if (affectedElementUri != null) {
for (EObject aw : phpArtifacts) {
String artifactUri = artifactAdapter.getArtifactUri(aw);
// Only create a marker if a signature of a
// method/variable/class... has changed inside of a source
// file.
IssueType[] markersToDelete = null;
String deleteMarkerUri = "";
IModelElement element = DLTKCore.create(artifactUri);
if (element != null && element.exists()) {
deleteMarkerUri = artifactUri;
markersToDelete = new IssueType[] { IssueType.MOVED, IssueType.RENAMED, IssueType.DELETED };
} else if (issueType == IssueType.DELETED) {
markersToDelete = new IssueType[] { IssueType.MOVED, IssueType.RENAMED, IssueType.CHANGED };
deleteMarkerUri = affectedElementUri;
}
if (!deleteMarkerUri.isEmpty() && artifactUri.contains(deleteMarkerUri))
CapraNotificationHelper.deleteCapraMarker(artifactUri, markersToDelete, wrapperContainer);
if (artifactUri.contains(affectedElementUri)) {
HashMap<String, String> markerInfo = generateMarkerInfo(aw, delta, issueType);
CapraNotificationHelper.createCapraMarker(markerInfo, wrapperContainer);
}
}
}
}
}
/**
* Generates the attributes that will later be assigned (in the createMarker
* method) to a Capra change marker.
*
* @param aw
* ArtifactWrapper that links to the element in the delta or to a
* child of the element in the delta
* @param delta
* represents changes in the state of a PHP Element
* @param issue
* the type of change that occurred
* @return a key value HashMap, containing the attributes to be assigned to
* a Capra change marker and their keys (IDs).
*/
// This works well for child elements if files are renamed or deleted.
// Renaming classes/methods/variables inside a class
// translates to a deleted classes/methods/variables because the delta does
// not keep track of
// the new name of the class. It just knows about the old name.
private HashMap<String, String> generateMarkerInfo(EObject aw, IModelElementDelta delta, IssueType issueType) {
HashMap<String, String> markerInfo = new HashMap<String, String>();
// Properties from the PHP element in the wrapper
String oldArtifactUri = artifactAdapter.getArtifactUri(aw);
String oldArtifactName = artifactAdapter.getArtifactName(aw);
// Properties from the affected PHP element with its former path
String oldAffectedElementUri = delta.getElement().getHandleIdentifier();
String oldAffectedElementName = delta.getElement().getElementName();
String newAffectedElementUri = null;
String newAffectedElementName = null;
// Affected element with its new path (null if not renamed or moved)
IModelElement newAffectedElement = delta.getMovedToElement();
if (newAffectedElement != null) {
newAffectedElementUri = newAffectedElement.getHandleIdentifier();
newAffectedElementName = newAffectedElement.getElementName();
}
String message = "";
// TODO Make messages more readable (replaces uris with names?).
switch (issueType) {
case RENAMED:
if (oldArtifactUri.equals(oldAffectedElementUri))
// The element in the wrapper is the renamed element.
message = oldAffectedElementUri + " has been renamed to " + newAffectedElementUri + ".";
else
// The element in the wrapper is a child of the renamed element.
message = oldAffectedElementName + ", an ancestor of " + oldArtifactUri + ", has been renamed to "
+ newAffectedElementName + ".";
break;
case MOVED:
if (oldArtifactUri.equals(oldAffectedElementUri))
// The element in the wrapper is the moved element.
message = oldAffectedElementUri + " has been moved to " + newAffectedElementUri + ".";
else
// The element in the wrapper is a child of the moved element.
message = oldAffectedElementName + ", an ancestor of " + oldArtifactUri + ", has been moved to "
+ newAffectedElementUri;
break;
default:
IModelElement el = DLTKCore.create(oldArtifactUri);
if (el == null || !el.exists()) {
issueType = IssueType.DELETED;
message = artifactAdapter.getArtifactUri(aw);
if (el instanceof ISourceReference && !(el instanceof ISourceModule))
message += " has been deleted or has had its signature changed.";
else
message += " has been deleted.";
}
break;
}
// The affected element has been renamed or moved.
if (newAffectedElementUri != null) {
String newArtifactUri;
String newArtifactName;
if (oldArtifactUri.equals(oldAffectedElementUri)) {
// The element in the wrapper is the affected element.
newArtifactUri = newAffectedElementUri;
newArtifactName = newAffectedElementName;
} else {
// The element in the wrapper is a child of the affected
// element.
// Build new uri for the moved class
newArtifactUri = oldArtifactUri.replace(oldAffectedElementUri, newAffectedElementUri);
newArtifactName = oldArtifactName;
// Check if artifact belongs to the PHP files (because
// inner elements in the PHP file change with the file name)
if (newAffectedElement instanceof ISourceModule) {
String oldPublicClassName = oldAffectedElementName.replace(".php", "");
String newPublicClassName = newAffectedElementName.replace(".php", "");
int classStartIndex = newAffectedElementUri.length();
int classEndIndex = classStartIndex + oldPublicClassName.length();
if (newArtifactUri.substring(classStartIndex, classEndIndex).equals(oldPublicClassName))
newArtifactUri = newArtifactUri.substring(0, classStartIndex) + newPublicClassName
+ newArtifactUri.substring(classEndIndex);
if (newArtifactName.contentEquals(oldPublicClassName))
newArtifactName = newPublicClassName;
}
}
markerInfo.put(CapraNotificationHelper.NEW_URI, newArtifactUri);
markerInfo.put(CapraNotificationHelper.NEW_NAME, newArtifactName);
}
markerInfo.put(CapraNotificationHelper.ISSUE_TYPE, issueType.getValue());
markerInfo.put(CapraNotificationHelper.OLD_URI, oldArtifactUri);
markerInfo.put(CapraNotificationHelper.MESSAGE, message);
return markerInfo;
}
}