/*******************************************************************************
* Copyright (c) 2011 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is 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:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.common.ui.marker;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.jdt.core.IAnnotatable;
import org.eclipse.jdt.core.IAnnotation;
import org.eclipse.jdt.core.IBuffer;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.ILocalVariable;
import org.eclipse.jdt.core.IMemberValuePair;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.ISourceReference;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.refactoring.CompilationUnitChange;
import org.eclipse.jdt.internal.core.JavaElement;
import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jpt.common.core.internal.utility.jdt.ASTTools;
import org.eclipse.ltk.core.refactoring.TextChange;
import org.eclipse.ltk.core.refactoring.TextFileChange;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.text.edits.InsertEdit;
import org.eclipse.text.edits.ReplaceEdit;
import org.eclipse.ui.IMarkerResolution2;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.jboss.tools.common.EclipseUtil;
import org.jboss.tools.common.preferences.SeverityPreferences;
import org.jboss.tools.common.refactoring.MarkerResolutionUtils;
import org.jboss.tools.common.ui.CommonUIMessages;
import org.jboss.tools.common.ui.CommonUIPlugin;
import org.jboss.tools.common.validation.WarningNameManager;
import org.osgi.service.prefs.BackingStoreException;
/**
* @author Daniel Azarov
*/
public class AddSuppressWarningsMarkerResolution implements
IMarkerResolution2, IJavaCompletionProposal {
public static final String SUPPRESS_WARNINGS_ANNOTATION = "SuppressWarnings";
public static final String SPACE = " "; //$NON-NLS-1$
public static final String DOT = "."; //$NON-NLS-1$
public static final String AT = "@"; //$NON-NLS-1$
private static final String PROBLEM_ID = JavaCore.COMPILER_PB_UNHANDLED_WARNING_TOKEN;
private SP preferences = new SP();
private IFile file;
private IAnnotatable element;
private String preferenceKey;
private String label;
private String description;
private ICompilationUnit cu;
public AddSuppressWarningsMarkerResolution(IFile file, IJavaElement element, String preferenceKey){
this.file = file;
this.element = getAnnatatableElement(element);
String[] names = WarningNameManager.getInstance().getWarningNames(preferenceKey);
if(names != null && names.length > 0){
this.preferenceKey = names[0];
}else{
this.preferenceKey = null;
}
label = NLS.bind(CommonUIMessages.ADD_SUPPRESS_WARNINGS_TITLE, this.preferenceKey, element.getElementName());
if(element instanceof IMethod){
label += "()";
}
description = getPreview();
}
public AddSuppressWarningsMarkerResolution(IFile file, IJavaElement element, String preferenceKey, ICompilationUnit compilationUnit){
this(file, element, preferenceKey);
this.cu = compilationUnit;
description = getPreview();
}
private String getPreview(){
TextChange previewChange = getPreviewChange();
try {
return MarkerResolutionUtils.getPreview(previewChange);
} catch (CoreException e) {
CommonUIPlugin.getDefault().logError(e);
}
return label;
}
private IAnnotatable getAnnatatableElement(IJavaElement element){
IJavaElement elem = element;
while(elem != null){
if(element instanceof IAnnotatable){
return (IAnnotatable) element;
}
elem = elem.getParent();
}
return null;
}
@Override
public String getLabel() {
return label;
}
private TextChange getPreviewChange(){
if(element != null && preferenceKey != null){
try {
ICompilationUnit original = (cu != null) ? cu : EclipseUtil.getCompilationUnit(file);
if(original == null) {
return null;
}
ICompilationUnit compilationUnit = original.getWorkingCopy(new NullProgressMonitor());
TextChange change = getChange(compilationUnit);
compilationUnit.discardWorkingCopy();
return change;
} catch (JavaModelException e) {
CommonUIPlugin.getDefault().logError(e);
}
}
return null;
}
private CompilationUnitChange getChange(ICompilationUnit compilationUnit) throws JavaModelException{
CompilationUnit cuNode = ASTTools.buildASTRoot(compilationUnit);
IJavaElement workingCopyElement = findWorkingCopy(compilationUnit, (IJavaElement)element);
ASTNode elementNode = null;
if(workingCopyElement instanceof JavaElement){
elementNode = ((JavaElement) workingCopyElement).findNode(cuNode);
}
IAnnotation annotation = findAnnotation(workingCopyElement);
CompilationUnitChange change = null;
if(annotation != null){
change = updateAnnotation(SUPPRESS_WARNINGS_ANNOTATION, preferenceKey, compilationUnit, annotation);
}else{
change = addAnnotation(SUPPRESS_WARNINGS_ANNOTATION+"(\""+preferenceKey+"\")", compilationUnit, workingCopyElement, elementNode);
}
return change;
}
@Override
public void run(IMarker marker) {
ICompilationUnit original = EclipseUtil.getCompilationUnit(file);
if(original != null) {
do_run(original, false);
}
}
private void do_run(ICompilationUnit original, boolean leaveDirty){
if(element != null && preferenceKey != null && original != null){
disablePreference();
try {
ICompilationUnit compilationUnit = original.getWorkingCopy(new NullProgressMonitor());
CompilationUnitChange change = getChange(compilationUnit);
if(change != null){
if(leaveDirty){
change.setSaveMode(TextFileChange.LEAVE_DIRTY);
}
change.perform(new NullProgressMonitor());
original.reconcile(ICompilationUnit.NO_AST, false, null, new NullProgressMonitor());
}
compilationUnit.discardWorkingCopy();
} catch (JavaModelException e) {
CommonUIPlugin.getDefault().logError(e);
} catch (CoreException e) {
CommonUIPlugin.getDefault().logError(e);
}
}
}
private IAnnotation findAnnotation(IJavaElement workingCopyElement) throws JavaModelException{
IAnnotation annotation = ((IAnnotatable)workingCopyElement).getAnnotation(SUPPRESS_WARNINGS_ANNOTATION);
if(annotation != null && annotation.exists()){
return annotation;
}
return null;
}
public static String getShortName(String qualifiedName){
int lastDot = qualifiedName.lastIndexOf(DOT);
String name;
if(lastDot < 0)
name = qualifiedName;
else
name = qualifiedName.substring(lastDot+1);
return name;
}
private void disablePreference(){
String value = preferences.getProjectPreference(file.getProject(), PROBLEM_ID);
if(!SeverityPreferences.IGNORE.equals(value)){
IEclipsePreferences projectPreferences = preferences.getProjectPreferences(file.getProject());
String projectValue = null;
if(projectPreferences != null){
projectValue = projectPreferences.get(PROBLEM_ID, null);
}
if(projectValue != null){
MessageDialog dialog = new MessageDialog(getShell(), label, null,
CommonUIMessages.ADD_SUPPRESS_WARNINGS_MESSAGE+
CommonUIMessages.ADD_SUPPRESS_WARNINGS_QUESTION1,
MessageDialog.QUESTION_WITH_CANCEL,
new String[]{CommonUIMessages.ADD_SUPPRESS_WARNINGS_CANCEL, CommonUIMessages.ADD_SUPPRESS_WARNINGS_DISABLE},
0);
int result = dialog.open();
if(result == 1){
IEclipsePreferences ePrefs = preferences.getProjectPreferences(file.getProject());
ePrefs.put(PROBLEM_ID, SeverityPreferences.IGNORE);
try {
ePrefs.flush();
} catch (BackingStoreException e) {
CommonUIPlugin.getDefault().logError(e);
}
}
}else{
MessageDialog dialog = new MessageDialog(getShell(), label, null,
CommonUIMessages.ADD_SUPPRESS_WARNINGS_MESSAGE+
NLS.bind(CommonUIMessages.ADD_SUPPRESS_WARNINGS_QUESTION2, file.getProject().getName()),
MessageDialog.QUESTION_WITH_CANCEL,
new String[]{CommonUIMessages.ADD_SUPPRESS_WARNINGS_CANCEL, CommonUIMessages.ADD_SUPPRESS_WARNINGS_WORKSPACE, CommonUIMessages.ADD_SUPPRESS_WARNINGS_PROJECT},
0);
int result = dialog.open();
if(result == 1){
IEclipsePreferences ePrefs = preferences.getInstancePreferences();
ePrefs.put(PROBLEM_ID, SeverityPreferences.IGNORE);
try {
ePrefs.flush();
} catch (BackingStoreException e) {
CommonUIPlugin.getDefault().logError(e);
}
}else if(result == 2){
IEclipsePreferences ePrefs = preferences.getProjectPreferences(file.getProject());
ePrefs.put(PROBLEM_ID, SeverityPreferences.IGNORE);
try {
ePrefs.flush();
} catch (BackingStoreException e) {
CommonUIPlugin.getDefault().logError(e);
}
}
}
}
}
private static Shell getShell() {
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
if (window == null) {
IWorkbenchWindow[] windows = PlatformUI.getWorkbench().getWorkbenchWindows();
if (windows.length > 0) {
return windows[0].getShell();
}
}
else {
return window.getShell();
}
return null;
}
@Override
public String getDescription() {
return description;
}
@Override
public Image getImage() {
String key = "ADD_ANNOTATION";
ImageRegistry registry = CommonUIPlugin.getDefault().getImageRegistry();
Image image = registry.get(key);
if (image == null) {
try {
image = ImageDescriptor.createFromURL(
new URL(CommonUIPlugin.getDefault().getBundle()
.getEntry("/"), "icons/add_annotation.png")).createImage();
registry.put(key, image);
} catch (MalformedURLException e) {
CommonUIPlugin.getDefault().logError(e);
}
}
return image;
}
private CompilationUnitChange updateAnnotation(String name, String parameter, ICompilationUnit compilationUnit, IAnnotation annotation) throws JavaModelException{
String str = AT+name;
str += "({";
for(IMemberValuePair pair : annotation.getMemberValuePairs()){
if(pair.getValueKind() == IMemberValuePair.K_STRING){
Object value = pair.getValue();
if(value instanceof String){
if(value.toString().equals(parameter)){
return null;
}
str += "\""+value+"\", ";
}else if(value instanceof Object[]){
Object[] array = (Object[])value;
for(Object a : array){
if(a instanceof String){
if(a.toString().equals(parameter)){
return null;
}
str += "\""+a+"\", ";
}
}
}
} else if(pair.getValueKind() == IMemberValuePair.K_QUALIFIED_NAME || pair.getValueKind() == IMemberValuePair.K_SIMPLE_NAME){
Object value = pair.getValue();
if(value instanceof String){
str += value+", ";
}else if(value instanceof Object[]){
Object[] array = (Object[])value;
for(Object a : array){
if(a instanceof String){
str += a+", ";
}
}
}
}
}
str += "\""+parameter+"\"";
str += "})";
CompilationUnitChange change = new CompilationUnitChange("", compilationUnit);
ReplaceEdit edit = new ReplaceEdit(annotation.getSourceRange().getOffset(), annotation.getSourceRange().getLength(), str);
change.setEdit(edit);
return change;
}
private CompilationUnitChange addAnnotation(String name, ICompilationUnit compilationUnit, IJavaElement element, ASTNode node) throws JavaModelException{
if(!(element instanceof ISourceReference))
return null;
ISourceReference workingCopySourceReference = (ISourceReference) element;
IBuffer buffer = compilationUnit.getBuffer();
int position = workingCopySourceReference.getSourceRange().getOffset();
if(node != null){
position = node.getStartPosition();
if(node instanceof BodyDeclaration && ((BodyDeclaration)node).getJavadoc() != null){
position += ((BodyDeclaration)node).getJavadoc().getLength();
char c = buffer.getChar(position);
while((c == '\r' || c == '\n') && position < buffer.getLength()-2 ){
position++;
c = buffer.getChar(position);
}
}
while(position < buffer.getLength()-1){
char c = buffer.getChar(position);
if(c != '\r' && c != '\n' && c != ' ' && c != '\t'){
break;
}
position++;
}
}
String str = AT+name;
if(!(workingCopySourceReference instanceof ILocalVariable)){
str += compilationUnit.findRecommendedLineSeparator();
int index = position;
while(index >= 0){
char c = buffer.getChar(index);
if(c == '\r' || c == '\n')
break;
index--;
}
index++;
if(index < position){
String spaces = buffer.getText(index, position-index);
str += spaces;
}
}else{
str += SPACE;
}
CompilationUnitChange change = new CompilationUnitChange("", compilationUnit);
InsertEdit edit = new InsertEdit(position, str);
change.setEdit(edit);
return change;
}
@SuppressWarnings("unchecked")
private static <T extends IJavaElement> T findWorkingCopy(ICompilationUnit compilationUnit, T element) throws JavaModelException{
if(element instanceof IAnnotation){
IJavaElement parent = findWorkingCopy(compilationUnit, element.getParent());
if(parent instanceof IAnnotatable){
for(IAnnotation a : ((IAnnotatable)parent).getAnnotations()){
if(a.getElementName().equals(element.getElementName()))
return (T)a;
}
}
}else if(element instanceof ILocalVariable && ((ILocalVariable) element).isParameter()){
IJavaElement parent = findWorkingCopy(compilationUnit, element.getParent());
if(parent instanceof IMethod){
for(ILocalVariable parameter : ((IMethod)parent).getParameters()){
if(parameter.getElementName().equals(element.getElementName()) && parameter.getTypeSignature().equals(((ILocalVariable)element).getTypeSignature()))
return (T)parameter;
}
}
}else{
IJavaElement[] elements = compilationUnit.findElements(element);
if(elements != null){
for(IJavaElement e : elements){
if(e.getClass().equals(element.getClass()))
return (T)e;
}
}
}
return null;
}
static class SP extends SeverityPreferences{
@Override
protected Set<String> getSeverityOptionNames() {
return null;
}
@Override
protected String createSeverityOption(String shortName) {
return null;
}
@Override
protected String getPluginId() {
return JavaCore.PLUGIN_ID;
}
}
@Override
public void apply(IDocument document) {
do_run(cu, true);
}
@Override
public Point getSelection(IDocument document) {
return null;
}
@Override
public String getAdditionalProposalInfo() {
return description;
}
@Override
public String getDisplayString() {
return label;
}
@Override
public IContextInformation getContextInformation() {
return null;
}
@Override
public int getRelevance() {
return 100;
}
}