blob: 11840b925a172fc87847eb952b545286f5b1e3ec [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2001, 2004 IBM Corporation 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.wst.validation.internal;
import java.lang.reflect.InvocationTargetException;
import java.util.Iterator;
import java.util.Set;
import java.util.logging.Level;
import org.eclipse.core.resources.ICommand;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.wst.validation.core.SeverityEnum;
import org.eclipse.wst.validation.internal.operations.ValidatorManager;
import org.eclipse.wst.validation.internal.plugin.ValidationPlugin;
import com.ibm.wtp.common.logger.LogEntry;
import com.ibm.wtp.common.logger.proxy.Logger;
/**
* This class migrates the following: 1. if a validator class name changes, its old messages must be
* updated to use the new class name
*
* Also, if a validator is not installed any more, its old messages are removed by this class'
* removeOrphanedTasks() method.
*/
public final class ValidationMigrator implements ConfigurationConstants {
private static ValidationMigrator _inst = null;
private final static IMarker[] NO_MARKERS = new IMarker[0];
private IWorkspaceRunnable _workspaceMigrator = null;
private IWorkspaceRunnable _projectMigrator = null;
// This interface is needed so that migration of an IProject can be done inside
// an IWorkspaceRunnable (i.e., one build instead of many).
interface ProjectMigrator extends IWorkspaceRunnable {
public void setProject(IProject project);
public IProject getProject();
}
private ValidationMigrator() {
//Default constructor
}
public static ValidationMigrator singleton() {
if (_inst == null) {
_inst = new ValidationMigrator();
}
return _inst;
}
private IWorkspaceRunnable getGlobalMigrator() {
if (_workspaceMigrator == null) {
_workspaceMigrator = new IWorkspaceRunnable() {
public void run(IProgressMonitor monitor) {
try {
// Whether the workspace has been migrated or not, check for orphan markers
// and remove them.
IWorkspaceRoot root = TaskListUtility.getRoot();
removeOrphanTasks(monitor, root);
GlobalConfiguration gp = ConfigurationManager.getManager().getGlobalConfiguration();
if (gp.isVersionCurrent()) {
// Workspace has already been migrated. Don't re-migrate the workspace,
// and don't re-migrate the projects in the workspace.
// 1. Since this is the current version, all open projects have already
// been migrated once.
// 2. Any project that isn't open can't be migrated
// 3. Any project that was opened after the workspace was migrated is
// itself migrated on the "OPEN" notification
return;
}
// Do not migrate all of the validators or the projects; let the project
// migration take care of that step once validation has been awoken on
// the project.
// Once all of the migration is complete, mark the preference as current.
gp.markVersionCurrent();
} catch (InvocationTargetException exc) {
Logger logger = ValidationPlugin.getPlugin().getMsgLogger();
if (logger.isLoggingLevel(Level.SEVERE)) {
LogEntry entry = ValidationPlugin.getLogEntry();
entry.setSourceIdentifier("ValidationMigrator.migrate"); //$NON-NLS-1$
entry.setTargetException(exc);
logger.write(Level.SEVERE, entry);
if (exc.getTargetException() != null) {
entry.setTargetException(exc.getTargetException());
logger.write(Level.SEVERE, entry);
}
}
}
}
};
}
return _workspaceMigrator;
}
private IWorkspaceRunnable getProjectMigrator(IProject project) {
if (_projectMigrator == null) {
_projectMigrator = new ProjectMigrator() {
private IProject _project = null;
public void setProject(IProject aProject) {
_project = aProject;
}
public IProject getProject() {
return _project;
}
public void run(IProgressMonitor monitor) {
try {
// orphan tasks (i.e., IMarkers that are corrupt) need to be removed
// periodically,
// instead of once when a project is migrated. If a project is migrated but
// has
// orphan tasks, the user can close & reopen the project to have the orphan
// tasks
// removed.
removeOrphanTasks(monitor, getProject());
ProjectConfiguration prjp = ConfigurationManager.getManager().getProjectConfiguration(getProject());
if (prjp.isVersionCurrent()) {
// Project has already been migrated.
return;
}
migrateValidator(monitor, ValidationRegistryReader.getReader().getValidatorMetaData(getProject()), getProject());
migrateBuilder(monitor, getProject());
// Once all of the migration is complete, migrate the version number of the
// preferences
prjp.markVersionCurrent();
} catch (InvocationTargetException exc) {
Logger logger = ValidationPlugin.getPlugin().getMsgLogger();
if (logger.isLoggingLevel(Level.SEVERE)) {
LogEntry entry = ValidationPlugin.getLogEntry();
entry.setSourceIdentifier("ValidationMigrator.migrateBuilder"); //$NON-NLS-1$
entry.setTargetException(exc);
logger.write(Level.SEVERE, entry);
if (exc.getTargetException() != null) {
entry.setTargetException(exc.getTargetException());
logger.write(Level.SEVERE, entry);
}
}
}
}
};
}
((ProjectMigrator) _projectMigrator).setProject(project);
return _projectMigrator;
}
public void migrateRoot(IProgressMonitor monitor) {
boolean wasSuspended = ValidatorManager.getManager().isSuspended();
try {
ValidatorManager.getManager().suspendAllValidation(true); // don't validate when
// migrating
if (!ResourcesPlugin.getWorkspace().isTreeLocked())
ResourcesPlugin.getWorkspace().run(getGlobalMigrator(), monitor);
} catch (CoreException exc) {
Logger logger = ValidationPlugin.getPlugin().getMsgLogger();
if (logger.isLoggingLevel(Level.SEVERE)) {
LogEntry entry = ValidationPlugin.getLogEntry();
entry.setSourceIdentifier("ValidationMigrator::migrate"); //$NON-NLS-1$
entry.setTargetException(exc);
logger.write(Level.SEVERE, entry);
}
} finally {
ValidatorManager.getManager().suspendAllValidation(wasSuspended);
}
}
public void migrate(IProgressMonitor monitor, IProject project) {
boolean wasSuspended = ValidatorManager.getManager().isProjectSuspended(project);
try {
ValidatorManager.getManager().suspendValidation(project, true); // Don't run validation
// when migrating
if (!ResourcesPlugin.getWorkspace().isTreeLocked())
ResourcesPlugin.getWorkspace().run(getProjectMigrator(project), monitor);
} catch (CoreException exc) {
Logger logger = ValidationPlugin.getPlugin().getMsgLogger();
if (logger.isLoggingLevel(Level.SEVERE)) {
LogEntry entry = ValidationPlugin.getLogEntry();
entry.setSourceIdentifier("ValidationMigrator::migrate"); //$NON-NLS-1$
entry.setTargetException(exc);
logger.write(Level.SEVERE, entry);
}
} finally {
ValidatorManager.getManager().suspendValidation(project, wasSuspended);
}
}
/**
* This method has package instead of private visibility because if it were private, then the
* compiler needs to access this method via a synthetic accessor method, and that can have
* performance consequences.
*/
void migrateValidator(IProgressMonitor monitor, Set vmds, IResource resource) {
if (vmds == null) {
return;
}
Iterator iterator = vmds.iterator();
while (iterator.hasNext()) {
ValidatorMetaData vmd = (ValidatorMetaData) iterator.next();
migrateValidator(monitor, vmd, resource);
}
}
/**
* If the Validator has registered a name change, update all existing validation markers with
* the new name.
*/
private void migrateValidator(IProgressMonitor monitor, ValidatorMetaData vmd, IResource resource) {
ValidatorMetaData.MigrationMetaData mmd = vmd.getMigrationMetaData();
if (mmd == null) {
// no migration necessary
return;
}
Set idList = mmd.getIds();
if (idList == null) {
// nothing to migrate
return;
}
Iterator iterator = idList.iterator();
while (iterator.hasNext()) {
String[] ids = (String[]) iterator.next();
if (ids.length != 2) {
// log
continue;
}
String from = ids[0];
String to = ids[1];
if ((from == null) || (to == null)) {
// log
continue;
}
try {
TaskListUtility.updateOwner(from, to, resource);
} catch (CoreException exc) {
Logger logger = ValidationPlugin.getPlugin().getMsgLogger();
if (logger.isLoggingLevel(Level.SEVERE)) {
LogEntry entry = ValidationPlugin.getLogEntry();
entry.setSourceID("ValidationMigrator.migrateValidatorClass"); //$NON-NLS-1$
entry.setTargetException(exc);
logger.write(Level.SEVERE, entry);
}
}
}
}
/**
* This method removes all orphaned validation markers (i.e., with no "owner" attribute set or
* when the validator isn't installed any more).
*
* This method has package instead of private visibility because if it were private, then the
* compiler needs to access this method via a synthetic accessor method, and that can have
* performance consequences.
*/
void removeOrphanTasks(IProgressMonitor monitor, IResource resource) {
// 1. Previous owner of "messageLimit" message was IReporter, but now is ValidatorManager.
// This will be taken care of by the "removeOrphanTasks" call, because
// ValidatorManager.isInternalOwner
// will return false for IReporter.class.toString();
try {
IMarker[] orphanTasks = getOrphanTasks(monitor, resource);
if (orphanTasks.length > 0) {
monitor.subTask(ResourceHandler.getExternalizedMessage(ResourceConstants.VBF_STATUS_REMOVING));
ResourcesPlugin.getWorkspace().deleteMarkers(orphanTasks);
monitor.subTask(ResourceHandler.getExternalizedMessage(ResourceConstants.VBF_STATUS_REMOVINGDONE));
}
} catch (CoreException exc) {
Logger logger = ValidationPlugin.getPlugin().getMsgLogger();
if (logger.isLoggingLevel(Level.SEVERE)) {
LogEntry entry = ValidationPlugin.getLogEntry();
entry.setSourceID("ValidationMigrator.removeOrphanTasks"); //$NON-NLS-1$
entry.setTargetException(exc);
logger.write(Level.SEVERE, entry);
}
}
}
/**
* Return any markers whose owners do not exist (either the validator is not installed any more,
* or the marker was created incorrectly).
*/
private IMarker[] getOrphanTasks(IProgressMonitor monitor, IResource resource) throws CoreException {
monitor.subTask(ResourceHandler.getExternalizedMessage(ResourceConstants.VBF_STATUS_LOOKING));
try {
if (resource == null) {
return NO_MARKERS;
}
int orphanCount = 0;
IMarker[] orphanList = null;
IMarker[] markers = TaskListUtility.getValidationTasks(resource, SeverityEnum.ALL_MESSAGES);
if (markers != null) {
orphanList = new IMarker[markers.length];
for (int i = 0; i < markers.length; i++) {
IMarker marker = markers[i];
Object owner = marker.getAttribute(VALIDATION_MARKER_OWNER);
// If the owner is an existing validator or a validation framework class, then
// it's
// not an orphaned task.
if ((owner == null) || !(owner instanceof String) || !((ValidationRegistryReader.getReader().isExistingValidator((String) owner) || ValidatorManager.getManager().isInternalOwner((String) owner)))) {
orphanList[orphanCount++] = marker;
}
}
}
if (orphanCount == 0) {
return NO_MARKERS;
}
IMarker[] result = new IMarker[orphanCount];
System.arraycopy(orphanList, 0, result, 0, orphanCount);
return result;
} finally {
monitor.subTask(ResourceHandler.getExternalizedMessage(ResourceConstants.VBF_STATUS_LOOKINGDONE));
}
}
/**
* This method has package instead of private visibility because if it were private, then the
* compiler needs to access this method via a synthetic accessor method, and that can have
* performance consequences.
*/
void migrateBuilder(IProgressMonitor monitor, IProject p) {
try {
ProjectConfiguration prjp = ConfigurationManager.getManager().getProjectConfiguration(p);
String version = prjp.getVersion();
if (!version.equals(VERSION4_03)) {
// builder was migrated already. Only 4.03 needs to have its id migrated.
return;
}
int newIdIndex = -1;
int oldIdIndex = -1;
if (p.exists() && p.isOpen()) {
IProjectDescription description = p.getDescription();
ICommand[] commands = description.getBuildSpec();
for (int j = 0; j < commands.length; j++) {
org.eclipse.core.resources.ICommand c = commands[j];
String name = c.getBuilderName();
if (name.equals("com.ibm.etools.j2ee.validationbuilder")) { //$NON-NLS-1$
oldIdIndex = j;
} else if (name.equals("org.eclipse.wst.validation.core.validationbuilder")) { //$NON-NLS-1$
newIdIndex = j;
}
}
if ((oldIdIndex > -1) && (newIdIndex > -1)) {
// Don't need to add the new, just delete the old.
ICommand[] newCommands = new ICommand[commands.length - 1];
if (oldIdIndex == 0) {
System.arraycopy(commands, 1, newCommands, 0, commands.length - 1);
} else if (oldIdIndex == commands.length) {
System.arraycopy(commands, 0, newCommands, 0, commands.length - 1);
} else {
System.arraycopy(commands, 0, newCommands, 0, oldIdIndex);
System.arraycopy(commands, oldIdIndex + 1, newCommands, oldIdIndex, commands.length - oldIdIndex - 1);
}
description.setBuildSpec(newCommands);
} else if (oldIdIndex > -1) {
// Delete the old and add the new.
ICommand command = description.newCommand();
command.setBuilderName("org.eclipse.wst.validation.core.validationbuilder"); //$NON-NLS-1$
commands[oldIdIndex] = command;
description.setBuildSpec(commands);
}
p.setDescription(description, null);
}
} catch (CoreException exc) {
// ignore. This is a temporary class anyways. Eventually everyone will move to a build
// where the builder doesn't exist.
} catch (InvocationTargetException exc) {
Logger logger = ValidationPlugin.getPlugin().getMsgLogger();
if (logger.isLoggingLevel(Level.SEVERE)) {
LogEntry entry = ValidationPlugin.getLogEntry();
entry.setSourceIdentifier("ValidationMigrator.migrateBuilder"); //$NON-NLS-1$
entry.setTargetException(exc);
logger.write(Level.SEVERE, entry);
if (exc.getTargetException() != null) {
entry.setTargetException(exc.getTargetException());
logger.write(Level.SEVERE, entry);
}
}
}
}
}