blob: 8057edbb6affbf5f4653e600ca89ec2971bed805 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2015 IBM Corporation and others.
*
* 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.ltk.core.refactoring;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
/**
* A <code>RefactoringStatus</code> object represents the outcome of a
* condition checking operation. It manages a list of <code>
* RefactoringStatusEntry</code> objects. Each <code>RefactoringStatusEntry
* </code> object describes one particular problem detected during
* condition checking.
* <p>
* Additionally a problem severity is managed. Severities are ordered as follows:
* <code>OK</code> &lt; <code>INFO</code> &lt; <code>WARNING</code> &lt; <code>
* ERROR</code> &lt; <code>FATAL</code>. The status's problem severity is the maximum
* of the severities of all entries. If the status doesn't have any entry the status's
* severity is <code>OK</code>.
* </p>
* <p>
* Note: this class is not intended to be extended by clients.
* </p>
*
* @see RefactoringStatusEntry
* @see Refactoring#checkAllConditions(IProgressMonitor)
*
* @since 3.0
*
* @noextend This class is not intended to be subclassed by clients.
*/
public class RefactoringStatus {
/**
* Status severity constant (value 0) indicating this status represents the nominal case.
*/
public static final int OK= 0;
/**
* Status severity constant (value 1) indicating this status is informational only.
*/
public static final int INFO= 1;
/**
* Status severity constant (value 2) indicating this status represents a warning.
* <p>
* Use this severity if the refactoring can be performed, but you assume that the
* user could not be aware of problems or confusions resulting from the execution.
* </p>
*/
public static final int WARNING= 2;
/**
* Status severity constant (value 3) indicating this status represents an error.
* <p>
* Use this severity if the refactoring can be performed, but the refactoring will
* not be behavior preserving and/or the partial execution will lead to an inconsistent
* state (e.g. compile errors).
* </p>
*/
public static final int ERROR= 3;
/**
* Status severity constant (value 4) indicating this status represents a fatal error.
* <p>
* Use this severity if the refactoring cannot be performed, and execution would lead
* to major problems. Note that this completely blocks the user from performing this refactoring.
* It is often preferable to use an {@link #ERROR} status and allow a partial execution
* (e.g. if just one reference to a refactored element cannot be updated).
* </p>
*/
public static final int FATAL= 4;
/**
* List of refactoring status entries.
*/
private List<RefactoringStatusEntry> fEntries;
/**
* The status's severity. The following invariant holds for
* <code>fSeverity</code>: <code>OK</code> &le; <code>fSeverity</code> &le;
* <code>FATAL</code>.
*/
private int fSeverity= OK;
/**
* Creates a new refactoring status with an empty list of
* status entries and a severity of <code>OK</code>.
*/
public RefactoringStatus() {
fEntries= new ArrayList<>(0);
}
/**
* Returns the severity.
*
* @return the severity.
*/
public int getSeverity() {
return fSeverity;
}
/**
* Returns the list of refactoring status entries.
*
* @return the list of refactoring status entries. Returns an empty array
* if no entries are managed.
*/
public RefactoringStatusEntry[] getEntries() {
return fEntries.toArray(new RefactoringStatusEntry[fEntries.size()]);
}
/**
* Returns a list of refactoring status entries which are considered equal
* to the specified status entry.
*
* @param comparator the comparator to determine whether two status entries
* are considered equal
* @param entry the refactoring status entry to compare the entries of this
* status with
* @return the list of refactoring status entries that are considered equal
* to the specified one, in no particular order. Returns an empty
* array if no entries are managed or none of them matches.
*
* @since 3.1
*/
public RefactoringStatusEntry[] getEntries(IRefactoringStatusEntryComparator comparator, RefactoringStatusEntry entry) {
final List<RefactoringStatusEntry> matches= new ArrayList<>(fEntries.size());
RefactoringStatusEntry current= null;
for (Iterator<RefactoringStatusEntry> iterator= fEntries.iterator(); iterator.hasNext();) {
current= iterator.next();
if (comparator.compare(current, entry) == 0)
matches.add(current);
}
return matches.toArray(new RefactoringStatusEntry[matches.size()]);
}
/**
* Returns whether the status has entries or not.
*
* @return <code>true</code> if the status as any entries; otherwise
* <code>false</code> is returned.
*/
public boolean hasEntries() {
return !fEntries.isEmpty();
}
/**
* Returns the <code>RefactoringStatusEntry</code> at the specified index.
*
* @param index the index of the entry to return
* @return the entry at the specified index
*
* @throws IndexOutOfBoundsException if the index is out of range
*/
public RefactoringStatusEntry getEntryAt(int index) {
return fEntries.get(index);
}
/**
* Returns the first entry managed by this refactoring status that
* matches the given plug-in identifier and code. If more than one
* entry exists that matches the criteria the first one in the list
* of entries is returned. Returns <code>null</code> if no entry
* matches.
*
* @param pluginId the entry's plug-in identifier
* @param code the entry's code
* @return the entry that matches the given plug-in identifier and
* code; <code>null</code> otherwise
*/
public RefactoringStatusEntry getEntryMatchingCode(String pluginId, int code) {
Assert.isTrue(pluginId != null);
for (RefactoringStatusEntry entry : fEntries) {
if (pluginId.equals(entry.getPluginId()) && entry.getCode() == code)
return entry;
}
return null;
}
/**
* Returns the first entry which severity is equal or greater than the
* given severity. If more than one entry exists that matches the
* criteria the first one is returned. Returns <code>null</code> if no
* entry matches.
*
* @param severity the severity to search for. Must be one of <code>FATAL
* </code>, <code>ERROR</code>, <code>WARNING</code> or <code>INFO</code>
* @return the entry that matches the search criteria
*/
public RefactoringStatusEntry getEntryMatchingSeverity(int severity) {
Assert.isTrue(severity >= OK && severity <= FATAL);
if (severity > fSeverity)
return null;
Iterator<RefactoringStatusEntry> iter= fEntries.iterator();
while (iter.hasNext()) {
RefactoringStatusEntry entry= iter.next();
if (entry.getSeverity() >= severity)
return entry;
}
return null;
}
/**
* Returns the entry with the highest severity. If there is more than one
* entry that matches the first one found in the list of entries is returned.
*
* @return the entry with the highest severity or <code>null</code> if no
* entries are present
*
* @since 3.1
*/
public RefactoringStatusEntry getEntryWithHighestSeverity() {
if (fEntries == null || fEntries.size() == 0)
return null;
RefactoringStatusEntry result= fEntries.get(0);
for (int i= 1; i < fEntries.size(); i++) {
RefactoringStatusEntry entry= fEntries.get(i);
if (result.getSeverity() < entry.getSeverity())
result= entry;
}
return result;
}
/**
* Returns the first message which severity is equal or greater than the
* given severity. If more than one entry exists that matches the criteria
* the first one is returned. Returns <code>null</code> if no entry matches.
*
* @param severity the severity to search for. Must be one of <code>FATAL
* </code>, <code>ERROR</code>, <code>WARNING</code> or <code>INFO</code>
* @return the message of the entry that matches the search criteria
*/
public String getMessageMatchingSeverity(int severity) {
RefactoringStatusEntry entry= getEntryMatchingSeverity(severity);
if (entry == null)
return null;
return entry.getMessage();
}
/**
* Creates a new <code>RefactoringStatus</code> with one entry filled with the given
* arguments.
*
* @param severity the severity
* @param msg the message
* @param context the context. Can be <code>null</code>
* @param pluginId the plug-in identifier. Can be <code>null</code> if argument <code>
* code</code> equals <code>NO_CODE</code>
* @param code the problem code. Must be either <code>NO_CODE</code> or a positive integer
* @param data application specific data
*
* @return the newly created refactoring status
*
* @see RefactoringStatusEntry
*/
public static RefactoringStatus createStatus(int severity, String msg, RefactoringStatusContext context, String pluginId, int code, Object data) {
RefactoringStatus result= new RefactoringStatus();
result.fEntries.add(new RefactoringStatusEntry(severity, msg, context, pluginId, code, data));
result.fSeverity= severity;
return result;
}
/**
* Creates a new <code>RefactoringStatus</code> with one <code>INFO</code> entry
* filled with the given message.
*
* @param msg the message of the info entry
* @return the refactoring status
*
* @see RefactoringStatusEntry
*/
public static RefactoringStatus createInfoStatus(String msg) {
return createStatus(INFO, msg, null, null, RefactoringStatusEntry.NO_CODE, null);
}
/**
* Creates a new <code>RefactoringStatus</code> with one <code>INFO</code> entry
* filled with the given message and context.
*
* @param msg the message of the info entry
* @param context the context of the info entry
* @return the refactoring status
*
* @see RefactoringStatusEntry
*/
public static RefactoringStatus createInfoStatus(String msg, RefactoringStatusContext context) {
return createStatus(INFO, msg, context, null, RefactoringStatusEntry.NO_CODE, null);
}
/**
* Creates a new <code>RefactoringStatus</code> with one <code>WARNING</code> entry
* filled with the given message.
*
* @param msg the message of the warning entry
* @return the refactoring status
*
* @see RefactoringStatusEntry
*/
public static RefactoringStatus createWarningStatus(String msg) {
return createStatus(WARNING, msg, null, null, RefactoringStatusEntry.NO_CODE, null);
}
/**
* Creates a <code>RefactoringStatus</code> with one <code>WARNING</code> entry
* fill with the given message and context.
*
* @param msg the message of the warning entry
* @param context the context of the warning entry
* @return the refactoring status
*
* @see RefactoringStatusEntry
*/
public static RefactoringStatus createWarningStatus(String msg, RefactoringStatusContext context) {
return createStatus(WARNING, msg, context, null, RefactoringStatusEntry.NO_CODE, null);
}
/**
* Creates a new <code>RefactoringStatus</code> with one <code>ERROR</code> entry
* filled with the given message.
*
* @param msg the message of the error entry
* @return the refactoring status
*
* @see RefactoringStatusEntry
*/
public static RefactoringStatus createErrorStatus(String msg) {
return createStatus(ERROR, msg, null, null, RefactoringStatusEntry.NO_CODE, null);
}
/**
* Creates a <code>RefactoringStatus</code> with one <code>ERROR</code> entry
* fill with the given message and context.
*
* @param msg the message of the error entry
* @param context the context of the error entry
* @return the refactoring status
*
* @see RefactoringStatusEntry
*/
public static RefactoringStatus createErrorStatus(String msg, RefactoringStatusContext context) {
return createStatus(ERROR, msg, context, null, RefactoringStatusEntry.NO_CODE, null);
}
/**
* Creates a new <code>RefactoringStatus</code> with one <code>FATAL</code> entry
* filled with the given message.
*
* @param msg the message of the fatal entry
* @return the refactoring status
*
* @see RefactoringStatusEntry
*/
public static RefactoringStatus createFatalErrorStatus(String msg) {
return createStatus(FATAL, msg, null, null, RefactoringStatusEntry.NO_CODE, null);
}
/**
* Creates a <code>RefactoringStatus</code> with one <code>FATAL</code> entry
* fill with the given message and context.
*
* @param msg the message of the fatal entry
* @param context the context of the fatal entry
* @return the refactoring status
*
* @see RefactoringStatusEntry
*/
public static RefactoringStatus createFatalErrorStatus(String msg, RefactoringStatusContext context) {
return createStatus(FATAL, msg, context, null, RefactoringStatusEntry.NO_CODE, null);
}
/**
* Creates a new <code>RefactoringStatus</code> from the given <code>IStatus</code>. An
* OK status is mapped to an OK refactoring status, an information status is mapped
* to a warning refactoring status, a warning status is mapped to an error refactoring
* status and an error or cancel status is mapped to a fatal refactoring status. An unknown
* status is converted into a fatal error status as well. If the status is a <code>MultiStatus
* </code> then the first level of children of the status will be added as refactoring status
* entries to the created refactoring status.
*
* @param status the status to create a refactoring status from
* @return the refactoring status
*
* @see IStatus
* @since 3.2
*/
public static RefactoringStatus create(IStatus status) {
if (status.isOK())
return new RefactoringStatus();
if (!status.isMultiStatus()) {
switch (status.getSeverity()) {
case IStatus.OK :
return new RefactoringStatus();
case IStatus.INFO :
return RefactoringStatus.createWarningStatus(status.getMessage());
case IStatus.WARNING :
return RefactoringStatus.createErrorStatus(status.getMessage());
case IStatus.ERROR :
return RefactoringStatus.createFatalErrorStatus(status.getMessage());
case IStatus.CANCEL :
return RefactoringStatus.createFatalErrorStatus(status.getMessage());
default :
return RefactoringStatus.createFatalErrorStatus(status.getMessage());
}
} else {
IStatus[] children= status.getChildren();
RefactoringStatus result= new RefactoringStatus();
for (IStatus child : children) {
result.merge(RefactoringStatus.create(child));
}
return result;
}
}
/**
* Merges the receiver and the parameter statuses. The resulting list of
* entries in the receiver will contain entries from both. The resulting
* severity in the receiver will be the more severe of its current severity
* and the parameter's severity. Merging with <code>null</code> is
* allowed - it has no effect.
*
* @param other the refactoring status to merge with
*/
public void merge(RefactoringStatus other) {
if (other == null)
return;
fEntries.addAll(other.fEntries);
fSeverity= Math.max(fSeverity, other.getSeverity());
}
/**
* Adds an <code>INFO</code> entry filled with the given message to this status.
* If the current severity is <code>OK</code> it will be changed to <code>INFO
* </code>. It will remain unchanged otherwise.
*
* @param msg the message of the info entry
*
* @see RefactoringStatusEntry
*/
public void addInfo(String msg) {
addInfo(msg, null);
}
/**
* Adds an <code>INFO</code> entry filled with the given message and context to
* this status. If the current severity is <code>OK</code> it will be changed to
* <code>INFO</code>. It will remain unchanged otherwise.
*
* @param msg the message of the info entry
* @param context the context of the info entry
*
* @see RefactoringStatusEntry
*/
public void addInfo(String msg, RefactoringStatusContext context) {
fEntries.add(new RefactoringStatusEntry(RefactoringStatus.INFO, msg, context));
fSeverity= Math.max(fSeverity, INFO);
}
/**
* Adds a <code>WARNING</code> entry filled with the given message to this status.
* If the current severity is <code>OK</code> or <code>INFO</code> it will be
* changed to <code>WARNING</code>. It will remain unchanged otherwise.
*
* @param msg the message of the warning entry
*
* @see RefactoringStatusEntry
*/
public void addWarning(String msg) {
addWarning(msg, null);
}
/**
* Adds a <code>WARNING</code> entry filled with the given message and context to
* this status. If the current severity is <code>OK</code> or <code>INFO</code> it
* will be changed to <code>WARNING</code>. It will remain unchanged otherwise.
*
* @param msg the message of the warning entry
* @param context the context of the warning entry
*
* @see RefactoringStatusEntry
*/
public void addWarning(String msg, RefactoringStatusContext context) {
fEntries.add(new RefactoringStatusEntry(RefactoringStatus.WARNING, msg, context));
fSeverity= Math.max(fSeverity, WARNING);
}
/**
* Adds an <code>ERROR</code> entry filled with the given message to this status.
* If the current severity is <code>OK</code>, <code>INFO</code> or <code>WARNING
* </code> it will be changed to <code>ERROR</code>. It will remain unchanged
* otherwise.
*
* @param msg the message of the error entry
*
* @see RefactoringStatusEntry
*/
public void addError(String msg) {
addError(msg, null);
}
/**
* Adds an <code>ERROR</code> entry filled with the given message and context to
* this status. If the current severity is <code>OK</code>, <code>INFO</code> or
* <code>WARNING</code> it will be changed to <code>ERROR</code>. It will remain
* unchanged otherwise.
*
* @param msg the message of the error entry
* @param context the context of the error entry
*
* @see RefactoringStatusEntry
*/
public void addError(String msg, RefactoringStatusContext context) {
fEntries.add(new RefactoringStatusEntry(RefactoringStatus.ERROR, msg, context));
fSeverity= Math.max(fSeverity, ERROR);
}
/**
* Adds a <code>FATAL</code> entry filled with the given message to this status.
* The severity of this status will changed to <code>FATAL</code>.
*
* @param msg the message of the fatal entry
*
* @see RefactoringStatusEntry
*/
public void addFatalError(String msg) {
addFatalError(msg, null);
}
/**
* Adds a <code>FATAL</code> entry filled with the given message and status to
* this status. The severity of this status will changed to <code>FATAL</code>.
*
* @param msg the message of the fatal entry
* @param context the context of the fatal entry
*
* @see RefactoringStatusEntry
*/
public void addFatalError(String msg, RefactoringStatusContext context) {
fEntries.add(new RefactoringStatusEntry(RefactoringStatus.FATAL, msg, context));
fSeverity= Math.max(fSeverity, FATAL);
}
/**
* Adds a new entry filled with the given arguments to this status. The severity
* of this status is set to the maximum of <code>fSeverity</code> and
* <code>severity</code>.
*
* @param severity the severity of the entry
* @param msg the message of the entry
* @param context the context of the entry. Can be <code>null</code>
* @param pluginId the plug-in identifier of the entry. Can be <code>null</code> if
* argument <code>code</code> equals <code>NO_CODE</code>
* @param code the problem code of the entry. Must be either <code>NO_CODE</code>
* or a positive integer
*/
public void addEntry(int severity, String msg, RefactoringStatusContext context, String pluginId, int code) {
fEntries.add(new RefactoringStatusEntry(severity, msg, context, pluginId, code));
fSeverity= Math.max(fSeverity, severity);
}
/**
* Adds a new entry filled with the given arguments to this status. The severity
* of this status is set to the maximum of <code>fSeverity</code> and
* <code>severity</code>.
*
* @param severity the severity of the entry
* @param msg the message of the entry
* @param context the context of the entry. Can be <code>null</code>
* @param pluginId the plug-in identifier of the entry. Can be <code>null</code> if
* argument <code>code</code> equals <code>NO_CODE</code>
* @param code the problem code of the entry. Must be either <code>NO_CODE</code>
* or a positive integer
* @param data application specific data of the entry
*/
public void addEntry(int severity, String msg, RefactoringStatusContext context, String pluginId, int code, Object data) {
fEntries.add(new RefactoringStatusEntry(severity, msg, context, pluginId, code, data));
fSeverity= Math.max(fSeverity, severity);
}
/**
* Adds the given <code>RefactoringStatusEntry</code>. The severity of this
* status is set to the maximum of <code>fSeverity</code> and the severity of
* the entry.
*
* @param entry the <code>RefactoringStatusEntry</code> to be added
*/
public void addEntry(RefactoringStatusEntry entry) {
Assert.isNotNull(entry);
fEntries.add(entry);
fSeverity= Math.max(fSeverity, entry.getSeverity());
}
/**
* Returns whether the status's severity is <code>OK</code> or not.
*
* @return <code>true</code> if the severity is <code>OK</code>;
* otherwise <code>false</code> is returned
*/
public boolean isOK() {
return fSeverity == OK;
}
/**
* Returns <code>true</code> if the current severity is <code>
* FATAL</code>.
*
* @return <code>true</code> if the current severity is <code>
* FATAL</code>; otherwise <code>false</code> is returned
*/
public boolean hasFatalError() {
return fSeverity == FATAL;
}
/**
* Returns <code>true</code> if the current severity is <code>
* FATAL</code> or <code>ERROR</code>.
*
* @return <code>true</code> if the current severity is <code>
* FATAL</code> or <code>ERROR</code>; otherwise <code>false
* </code> is returned
*/
public boolean hasError() {
return fSeverity == FATAL || fSeverity == ERROR;
}
/**
* Returns <code>true</code> if the current severity is <code>
* FATAL</code>, <code>ERROR</code> or <code>WARNING</code>.
*
* @return <code>true</code> if the current severity is <code>
* FATAL</code>, <code>ERROR</code> or <code>WARNING</code>;
* otherwise <code>false</code> is returned
*/
public boolean hasWarning() {
return fSeverity == FATAL || fSeverity == ERROR || fSeverity == WARNING;
}
/**
* Returns <code>true</code> if the current severity is <code>
* FATAL</code>, <code>ERROR</code>, <code>WARNING</code> or
* <code>INFO</code>.
*
* @return <code>true</code> if the current severity is <code>
* FATAL</code>, <code>ERROR</code>, <code>WARNING</code> or
* <code>INFO</code>; otherwise <code>false</code> is returned
*/
public boolean hasInfo() {
return fSeverity == FATAL || fSeverity == ERROR || fSeverity == WARNING || fSeverity == INFO;
}
/*
* (non java-doc)
* for debugging only
*/
@Override
public String toString() {
StringBuilder buff= new StringBuilder();
buff.append("<") //$NON-NLS-1$
.append(getSeverityString(fSeverity)).append("\n"); //$NON-NLS-1$
if (!isOK()) {
for (Iterator<RefactoringStatusEntry> iter= fEntries.iterator(); iter.hasNext(); ) {
buff.append("\t") //$NON-NLS-1$
.append(iter.next()).append("\n"); //$NON-NLS-1$
}
}
buff.append(">"); //$NON-NLS-1$
return buff.toString();
}
/*
* non java-doc
* for debugging only not for nls
*/
/* package */static String getSeverityString(int severity) {
Assert.isTrue(severity >= OK && severity <= FATAL);
if (severity == RefactoringStatus.OK)
return "OK"; //$NON-NLS-1$
if (severity == RefactoringStatus.INFO)
return "INFO"; //$NON-NLS-1$
if (severity == RefactoringStatus.WARNING)
return "WARNING"; //$NON-NLS-1$
if (severity == RefactoringStatus.ERROR)
return "ERROR"; //$NON-NLS-1$
if (severity == RefactoringStatus.FATAL)
return "FATALERROR"; //$NON-NLS-1$
return null;
}
}