package net.sourceforge.pmd.eclipse.ui.views;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import net.sourceforge.pmd.PMDConfiguration;
import net.sourceforge.pmd.PMDException;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.RuleSet;
import net.sourceforge.pmd.RuleSets;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.SourceCodeProcessor;
import net.sourceforge.pmd.eclipse.plugin.PMDPlugin;
import net.sourceforge.pmd.eclipse.ui.model.FileRecord;
import net.sourceforge.pmd.eclipse.ui.nls.StringKeys;
import net.sourceforge.pmd.eclipse.ui.views.ast.ASTUtil;
import net.sourceforge.pmd.lang.dfa.DFAGraphMethod;
import net.sourceforge.pmd.lang.dfa.DFAGraphRule;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.lang.java.dfa.JavaDFAGraphRule;
import net.sourceforge.pmd.util.StringUtil;
import org.eclipse.core.resources.IResource;
import org.eclipse.jface.resource.ColorRegistry;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.Page;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.themes.ITheme;
import org.eclipse.ui.themes.IThemeManager;
/**
*
* @author Brian Remedios
*/
public abstract class AbstractStructureInspectorPage extends Page implements IPropertyChangeListener, ISelectionChangedListener {
private Combo methodSelector;
private FileRecord resourceRecord;
private List<ASTMethodDeclaration> pmdMethodList;
private ITextEditor textEditor;
protected AbstractStructureInspectorPage(IWorkbenchPart part, FileRecord record) {
super();
resourceRecord = record;
if (part instanceof ITextEditor) {
textEditor = (ITextEditor) part;
}
}
public void refresh(IResource resource) {
if (resource.getType() == IResource.FILE) {
// set a new filerecord
resourceRecord = new FileRecord(resource);
}
}
// refresh the methods and select the old selected method
protected void refreshMethodSelector() {
int index = methodSelector.getSelectionIndex();
refreshPMDMethods();
showMethod(index);
methodSelector.select(index);
}
protected void highlight(int beginLine, int beginColumn, int endLine, int endColumn) {
try {
int offset = getDocument().getLineOffset(beginLine) + beginColumn;
int length = getDocument().getLineOffset(endLine) + endColumn - offset;
highlight(offset, length);
} catch (BadLocationException ble) {
logError(StringKeys.ERROR_RUNTIME_EXCEPTION + "Exception when selecting a section in the editor", ble);
}
}
protected IResource getResource() {
return resourceRecord.getResource();
}
protected void highlight(int offset, int length) {
if (textEditor == null) return;
textEditor.selectAndReveal(offset, length);
}
protected void highlightLine(int lineNumber) {
if (textEditor == null) return;
int offset = 0;
int length = 0;
try {
offset = getDocument().getLineOffset(lineNumber);
length = getDocument().getLineLength(lineNumber);
} catch (BadLocationException ble) {
logError(StringKeys.ERROR_RUNTIME_EXCEPTION + "Exception when selecting a line in the editor" , ble);
}
highlight(offset, length);
}
public void propertyChange(PropertyChangeEvent event) {
// TODO adapt the editors
System.out.println("property changed: " + event.getProperty());
}
public void dispose() {
super.dispose();
unregisterListeners();
}
private static ColorRegistry colorRegistry() {
IThemeManager themeManager = PlatformUI.getWorkbench().getThemeManager();
ITheme currentTheme = themeManager.getCurrentTheme();
return currentTheme.getColorRegistry();
}
protected void registerListeners() {
JFaceResources.getFontRegistry().addListener(this);
colorRegistry().addListener(this);
}
protected void unregisterListeners() {
JFaceResources.getFontRegistry().removeListener(this);
colorRegistry().removeListener(this);
}
public void setFocus() {
methodSelector.setFocus();
}
/**
* Shows the method that belongs to a violation (to a line).
* @param violation RuleViolation
*/
protected void showMethodToViolation(RuleViolation violation) {
final int beginLine = violation.getBeginLine();
for (int i = 0; i < pmdMethodList.size(); i++) {
ASTMethodDeclaration pmdMethod = pmdMethodList.get(i);
if (beginLine >= pmdMethod.getBeginLine() && beginLine <= pmdMethod.getEndLine()) {
showMethod(pmdMethod);
// select the method in the combobox
methodSelector.select(i);
return;
}
}
}
protected RuleViolation selectedViolationFrom(SelectionChangedEvent event) {
if (event.getSelection() instanceof IStructuredSelection) {
final Object element = ((IStructuredSelection) event.getSelection()).getFirstElement();
return element instanceof RuleViolation ?
(RuleViolation) element : null;
}
return null; // should never happen
}
/* @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent) */
public void selectionChanged(SelectionChangedEvent event) {
RuleViolation violation = selectedViolationFrom(event);
if (violation == null) return;
String varName = violation.getVariableName();
if (StringUtil.isEmpty(varName)) return;
int beginLine = violation.getBeginLine();
int endLine = violation.getEndLine();
if (beginLine != 0 && endLine != 0) {
try {
int offset = getDocument().getLineOffset(violation.getBeginLine() - 1);
int length = getDocument().getLineOffset(violation.getEndLine()) - offset;
textEditor.selectAndReveal(offset, length);
} catch (BadLocationException ble) {
logError(StringKeys.ERROR_RUNTIME_EXCEPTION + "Exception when selecting a line in the editor", ble);
}
// showMethodToMarker(marker);
showMethodToViolation(violation);
// then we calculate and color _a possible_ Path
// for this Error in the Dataflow
// final DataflowGraph graph = astViewer.getGraph();
// if (!astViewer.isDisposed() && graph != null) {
// graph.markPath(beginLine, endLine, varName);
// }
}
}
/**
* Refreshes the list of PMD methods for the combobox.
* @see #getPMDMethods(IResource)
*/
protected void refreshPMDMethods() {
methodSelector.removeAll();
pmdMethodList = getPMDMethods();
for (ASTMethodDeclaration pmdMethod : pmdMethodList) {
methodSelector.add(ASTUtil.getMethodLabel(pmdMethod, false));
}
}
public void showFirstMethod() {
methodSelector.select(0);
showMethod(0);
}
/**
* @return the underlying FileRecord
*/
public FileRecord getFileRecord() {
return resourceRecord;
}
/**
* Confort method to show a method.
* @param index index position of the combobox
*/
protected void showMethod(int index) {
if (index >= 0 && index < pmdMethodList.size() ) {
ASTMethodDeclaration method = pmdMethodList.get(index);
showMethod(method);
}
}
protected abstract void showMethod(ASTMethodDeclaration pmdMethod);
/**
* Helper method to return an NLS string from its key.
*/
protected static String getString(String key) {
return PMDPlugin.getDefault().getStringTable().getString(key);
}
protected void buildMethodSelector(Composite parent) {
// the drop down box for showing all methods of the given resource
methodSelector = new Combo(parent, SWT.DROP_DOWN | SWT.READ_ONLY | SWT.BORDER);
refreshPMDMethods();
methodSelector.setText(getString(StringKeys.VIEW_DATAFLOW_CHOOSE_METHOD));
methodSelector.setLayoutData(new GridData(300, SWT.DEFAULT));
methodSelector.addSelectionListener( new SelectionAdapter() {
/* @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent) */
public void widgetDefaultSelected(SelectionEvent e) {
if (methodSelector.equals(e.widget)) {
showMethod(methodSelector.getSelectionIndex());
}
}
public void widgetSelected(SelectionEvent e) {
methodPicked();
}
});
}
public void methodPicked() {
int index = methodSelector.getSelectionIndex();
methodSelector.setSelection(new Point(0,0));
showMethod(index);
}
/**
* Gets a List of all PMD-Methods.
*
* @return an List of ASTMethodDeclarations
*/
private List<ASTMethodDeclaration> getPMDMethods() {
List<ASTMethodDeclaration> methodList = new ArrayList<ASTMethodDeclaration>();
// we need PMD to run over the given Resource
// with the DFAGraphRule to get the Methods;
// PMD needs this Resource as a String
try {
DFAGraphRule dfaGraphRule = new JavaDFAGraphRule();
RuleSet rs = new RuleSet();
rs.addRule(dfaGraphRule);
RuleContext ctx = new RuleContext();
ctx.setSourceCodeFilename("[scratchpad]");
// StringReader reader = new StringReader(getDocument().get());
// run PMD using the DFAGraphRule and the Text of the Resource
// new PMDEngine().processFile(reader, rs, ctx);
byte[] bytes = getDocument().get().getBytes();
InputStream input = new ByteArrayInputStream(bytes);
RuleSets rSets = new RuleSets(rs);
new SourceCodeProcessor(new PMDConfiguration()).processSourceCode(input, rSets, ctx);
// the Rule then can give us the Methods
for (DFAGraphMethod m : dfaGraphRule.getMethods()) {
if (m instanceof ASTMethodDeclaration) {
methodList.add((ASTMethodDeclaration)m);
}
}
Collections.sort(methodList, ASTUtil.MethodComparator);
} catch (PMDException pmde) {
logError(StringKeys.ERROR_PMD_EXCEPTION + toString(), pmde);
}
return methodList;
}
protected void enableMethodSelector(boolean flag) {
methodSelector.setEnabled( flag );
}
/**
* Gets the Document of the page.
* @return instance of IDocument of the page
*/
public IDocument getDocument() {
return textEditor.getDocumentProvider().getDocument(textEditor.getEditorInput());
}
public static void logError(String message, Throwable error) {
PMDPlugin.getDefault().logError(message, error);
}
public static void logErrorByKey(String messageId, Throwable error) {
PMDPlugin.getDefault().logError(getString(messageId), error);
}
}