/*
* User: tom
* Date: Jul 3, 2002
* Time: 2:33:24 PM
*/
package net.sourceforge.pmd.jedit;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.BorderLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.*;
import java.util.*;
import java.util.regex.*;
import javax.swing.*;
import javax.swing.border.EtchedBorder;
import javax.swing.tree.DefaultMutableTreeNode;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.PMDConfiguration;
import net.sourceforge.pmd.Report;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.RuleSets;
import net.sourceforge.pmd.cpd.CPD;
import net.sourceforge.pmd.cpd.CPDConfiguration;
import net.sourceforge.pmd.util.FileFinder;
import net.sourceforge.pmd.cpd.AnyLanguage;
import net.sourceforge.pmd.cpd.JavaTokenizer;
import net.sourceforge.pmd.cpd.Language;
import net.sourceforge.pmd.cpd.LanguageFactory;
import net.sourceforge.pmd.cpd.Match;
import net.sourceforge.pmd.cpd.TokenEntry;
import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.renderers.CSVRenderer;
import net.sourceforge.pmd.renderers.HTMLRenderer;
import net.sourceforge.pmd.renderers.Renderer;
import net.sourceforge.pmd.renderers.TextRenderer;
import net.sourceforge.pmd.renderers.XMLRenderer;
import net.sourceforge.pmd.util.datasource.DataSource;
import net.sourceforge.pmd.util.designer.Designer;
import org.gjt.sp.jedit.Buffer;
import org.gjt.sp.jedit.EBMessage;
import org.gjt.sp.jedit.EBPlugin;
import org.gjt.sp.jedit.GUIUtilities;
import org.gjt.sp.jedit.Mode;
import org.gjt.sp.jedit.ServiceManager;
import org.gjt.sp.jedit.View;
import org.gjt.sp.jedit.jEdit;
import org.gjt.sp.jedit.browser.VFSBrowser;
import org.gjt.sp.jedit.io.VFSFile;
import org.gjt.sp.jedit.*;
import org.gjt.sp.jedit.msg.BufferUpdate;
import org.gjt.sp.jedit.syntax.ModeProvider;
import org.gjt.sp.util.Log;
import errorlist.DefaultErrorSource;
import errorlist.ErrorSource;
import ise.java.awt.KappaLayout;
/** jEdit plugin for PMD
* @version $Id$
*
* TODO: this really needs rewritten to move all the "instance" stuff to a new
* class.
**/
public class PMDJEditPlugin extends EBPlugin {
public static final String NAME = "PMD";
public static final String CHECK_DIR_RECURSIVE = "pmd.checkDirRecursive";
public static final String CUSTOM_RULES_ONLY_KEY = "pmd.customRulesOnly";
public static final String CUSTOM_RULES_PATH_KEY = "pmd.customRulesPath";
public static final String DEFAULT_TILE_MINSIZE_PROPERTY = "pmd.cpd.defMinTileSize";
public static final String EXCLUDE_CUSTOM_RULES_KEY = "pmd.excludeCustomRules";
public static final String IGNORE_LITERALS = "pmd.ignoreliterals";
public static final String LAST_DIRECTORY = "pmd.cpd.lastDirectory";
public static final String LAST_EXCLUSION_REGEX = "pmd.cpd.lastExclusionRegex";
public static final String LAST_INCLUSION_REGEX = "pmd.cpd.lastInclusionRegex";
public static final String LAST_SELECTED_FILTER = "pmd.cpd.lastSelectedFilter";
public static final String OPTION_RULES_PREFIX = "options.pmd.rules.";
public static final String PRINT_RULE = "pmd.printRule";
public static final String RUN_PMD_ON_SAVE = "pmd.runPMDOnSave";
public static final String CLEAR_ERRORLIST_ON_SAVE = "pmd.clearErrorListOnSave";
public static final String SHOW_PROGRESS = "pmd.showprogress";
private static PMDJEditPlugin instance;
private static PMDProgressWidgetFactory.ProgressWidget progressBar;
private Map<View, DefaultErrorSource> errorSources = new HashMap<View, DefaultErrorSource>();
public static final String RENDERER = "pmd.renderer";
private static int lastSelectedFilter = 0;
private static String lastInclusion = "";
private static String lastExclusion = "";
public static View MainView;
public void start() {
instance = this;
// Log.log(Log.DEBUG,this,"Instance created.");
lastSelectedFilter = jEdit.getIntegerProperty( LAST_SELECTED_FILTER, 0 );
lastInclusion = jEdit.getProperty( LAST_INCLUSION_REGEX, "" );
lastExclusion = jEdit.getProperty( LAST_EXCLUSION_REGEX, "" );
addProgressBar();
}
public void stop() {
instance = null;
unRegisterErrorSources();
ServiceManager.unregisterService( "org.gjt.sp.jedit.gui.statusbar.StatusWidgetFactory", "pmd" );
ServiceManager.unloadServices( getPluginJAR() );
removeProgressBar();
}
public static void checkDirectory( View view ) {
instance.MainView=view;
instance.instanceCheckDirectory( view );
}
public void handleMessage( EBMessage ebmess ) {
// maybe run PMD on buffer save
if ( ebmess instanceof BufferUpdate && jEdit.getBooleanProperty( PMDJEditPlugin.RUN_PMD_ON_SAVE ) ) {
try {
BufferUpdate bu = ( BufferUpdate ) ebmess;
String modename = bu.getBuffer().getMode().getName();
if ( bu.getWhat() == BufferUpdate.SAVED && ( "java".equals( modename ) || "jsp".equals( modename ) ) ) {
check( bu.getBuffer(), bu.getView() );
}
} catch ( Exception e ) { // NOPMD
}
}
}
// check all open buffers
public static void checkAllOpenBuffers( View view ) {
instance.MainView=view;
instance.instanceCheckAllOpenBuffers( view );
}
public void instanceCheckDirectory( final View view ) {
String[] paths = GUIUtilities.showVFSFileDialog( view, jEdit.getProperty( LAST_DIRECTORY ), VFSBrowser.CHOOSE_DIRECTORY_DIALOG, false );
try {
File selectedFile = null;
if ( paths != null && paths.length == 1 ) {
boolean recursive = JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog( view, "Recursively check subdirectories?", "Recursive", JOptionPane.YES_NO_OPTION );
selectedFile = new File( paths[0] );
if ( ! selectedFile.isDirectory() ) {
DefaultErrorSource errorSource = getErrorSource( view );
errorSource.addError( new DefaultErrorSource.DefaultError( errorSource, ErrorSource.ERROR, selectedFile.getAbsolutePath(), 0, 0, 0, jEdit.getProperty( "net.sf.pmd.Selection_not_a_directory.", "Selection not a directory." ) ) ); // NOPMD
return;
}
jEdit.setProperty( LAST_DIRECTORY, selectedFile.getCanonicalPath() );
jEdit.setBooleanProperty( CHECK_DIR_RECURSIVE, recursive );
process( selectedFile.getCanonicalPath(), view );
}
} catch ( IOException e ) {
Log.log( Log.DEBUG, this, e );
}
}
public void instanceCheckAllOpenBuffers( final View view ) {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
Buffer buffers[] = jEdit.getBuffers();
if ( buffers != null ) {
boolean showProgress = jEdit.getBooleanProperty( SHOW_PROGRESS );
if ( showProgress ) {
startProgressBarDisplay( view, buffers.length );
}
for ( Buffer buffer : buffers ) {
instanceCheck( buffer, view, false );
if ( showProgress && instance.progressBar != null ) {
instance.progressBar.update();
}
}
if ( showProgress ) {
endProgressBarDisplay();
}
}
}
} );
}
/**
* Runs the PMD rules on the given buffer.
* @param buffer The buffer to check.
* @param view The View displaying the buffer.
* @param clearErrorList Use <code>true</code> to clear the error list before displaying the results of this check.
*/
public void instanceCheck( Buffer buffer, View view, boolean clearErrorList ) {
String modename = buffer.getMode().getName();
LanguageVersion languageVersion = getLanguageVersion( modename );
if ( languageVersion == null ) {
return;
}
PMD pmd = new PMD();
// configure PMD to use the language parser for the buffer mode
PMDConfiguration configuration = pmd.getConfiguration();
configuration.setDefaultLanguageVersion( languageVersion );
// get the rules to run
SelectedRules selectedRuleSets = new SelectedRules();
RuleSets toCheck = selectedRuleSets.getSelectedRules();
SelectedRulesRuleSetFactory ruleFactory = new SelectedRulesRuleSetFactory( toCheck );
// wrap the buffer in a data source so it can be read as a stream
BufferDataSource source = new BufferDataSource( buffer );
List<DataSource> files = new ArrayList<DataSource>();
files.add( source );
// set up the rule context with a report to gather the processing output
RuleContext ctx = new RuleContext();
final Report report = new Report();
ctx.setReport( report );
String path = buffer.getPath();
ctx.setSourceCodeFilename( path );
ctx.setLanguageVersion( languageVersion );
// create an ErrorListRenderer that sends errors directly to ErrorList,
// doing so allows PMD use multiple threads if needed.
DefaultErrorSource errorSource = getErrorSource( view );
if ( clearErrorList ) {
errorSource.clear();
}
ErrorListRenderer errorListRenderer = new ErrorListRenderer( errorSource );
List<Renderer> renderers = new ArrayList<Renderer>();
renderers.add( errorListRenderer );
pmd.processFiles( configuration, ruleFactory, files, ctx, renderers );
}
public LanguageVersion getLanguageVersion( String modename ) {
if ( "java".equals( modename ) ) {
return LanguageVersion.JAVA_17;
}
if ( "c++".equals( modename ) ) {
return LanguageVersion.CPP;
}
if ( "javascript".equals( modename ) ) {
return LanguageVersion.ECMASCRIPT;
}
if ( "jsp".equals( modename ) ) {
return LanguageVersion.JSP;
}
if ( "php".equals( modename ) ) {
return LanguageVersion.PHP;
}
if ( "ruby".equals( modename ) ) {
return LanguageVersion.RUBY;
}
if ( "xml".equals( modename ) || "ant".equals( modename ) || "maven".equals( modename ) ) {
return LanguageVersion.XML;
}
if ( "xsl".equals( modename ) ) {
return LanguageVersion.XSL;
}
return null;
}
/**
* Run PMD on a directory of files. Only files of a type supported by PMD will be
* checked.
* @param files A list of files to check. It is assumed that these files are of
* a type supported by PMD.
* @param view The view for the current instance of PMD.
*/
public void process( final List<File> files, final View view ) {
processFiles( files, view );
}
/**
* Run PMD on a directory of files. Only files of a type supported by PMD will be
* checked.
* @param dirname The directory to search for files. Only supported files will be checked.
* @param view The view for the current instance of PMD.
*/
public void process( final String dirname, final View view ) {
SwingWorker<List<File>, Object> sw = new SwingWorker<List<File>, Object>() {
@Override
public List<File> doInBackground() {
List<File> files = findFiles( dirname, true );
return files;
}
@Override
public void done() {
try {
List<File> files = get();
processFiles( files, view );
} catch ( Exception e ) {
e.printStackTrace();
}
}
};
sw.execute();
}
// check current buffer
public static void check( Buffer buffer, View view ) {
instance.instanceCheck( buffer, view, jEdit.getBooleanProperty( CLEAR_ERRORLIST_ON_SAVE, false ) );
}
void processFiles( final List<File> files, final View view ) {
SwingWorker sw = new SwingWorker<Object, Object>() {
boolean showProgress = jEdit.getBooleanProperty( SHOW_PROGRESS );
@Override
public Object doInBackground() {
PMD pmd = new PMD();
PMDConfiguration configuration = pmd.getConfiguration();
configuration.setThreads(0 );
RuleContext ctx = new RuleContext();
// get the rules to run
SelectedRules selectedRuleSets = new SelectedRules();
RuleSets toCheck = selectedRuleSets.getSelectedRules();
SelectedRulesRuleSetFactory ruleFactory = new SelectedRulesRuleSetFactory( toCheck );
// create an ErrorListRenderer that sends errors directly to ErrorList,
// doing so allows PMD use multiple threads if needed.
DefaultErrorSource errorSource = getErrorSource( view );
errorSource.clear();
ErrorListRenderer errorListRenderer = new ErrorListRenderer( errorSource );
List<Renderer> renderers = new ArrayList<Renderer>();
renderers.add( errorListRenderer );
if ( showProgress ) {
startProgressBarDisplay( view, files.size() );
addPropertyChangeListener( new PropertyChangeListener() {
public void propertyChange( PropertyChangeEvent evt ) {
if ( "progress".equals( evt.getPropertyName() ) ) {
instance.progressBar.update();
}
}
} );
}
// TODO: pmd.processFiles can handle multiple files at once, can I use that here?
// -- maybe, once the pmd.processFiles with the progress monitor is complete.
for ( int count = 0; count < files.size(); count++ ) {
File file = files.get( count );
List<DataSource> fileToCheck = new ArrayList<DataSource>();
fileToCheck.add( new FileDataSource( file ) );
String modename = ModeProvider.instance.getModeForFile( file.getName(), "" ).getName();
LanguageVersion languageVersion = getLanguageVersion( modename );
// configure PMD to use the language parser for the buffer mode
configuration.setDefaultLanguageVersion( languageVersion );
ctx.setLanguageVersion( languageVersion );
ctx.setSourceCodeFile( file );
ctx.setReport( new Report() );
pmd.processFiles( configuration, ruleFactory, fileToCheck, ctx, renderers );
if ( showProgress && instance.progressBar != null ) {
setProgress( count );
}
}
return null;
}
@Override
public void done() {
if ( showProgress ) {
endProgressBarDisplay();
}
}
};
sw.execute();
}
private List<File> findFiles( String dir, boolean recurse ) {
FileFinder f = new FileFinder();
FilenameFilter filter = new FilenameFilter() {
public boolean accept( File dir, String name ) {
File file = new File( dir, name );
if ( file.isDirectory() ) {
return true;
}
Mode mode = ModeProvider.instance.getModeForFile( name, "" );
String modename = mode == null ? "" : mode.getName();
return getLanguageVersion( modename ) != null;
}
};
return f.findFilesFrom( dir, filter, recurse );
}
private void unRegisterErrorSources() {
for ( DefaultErrorSource errorSource : errorSources.values() ) {
ErrorSource.unregisterErrorSource( errorSource );
}
}
private DefaultErrorSource getErrorSource( View view ) {
DefaultErrorSource errorSource = errorSources.get( view );
if ( errorSource == null ) {
errorSource = new DefaultErrorSource( NAME, view );
errorSources.put( view, errorSource );
ErrorSource.registerErrorSource( errorSource );
}
return errorSource;
}
public static void cpdCurrentFile( View view ) throws IOException {
instance.MainView=view;
String modeName = getFileType( view.getBuffer().getMode().getName() );
if ( modeName == null ) {
JOptionPane.showMessageDialog( view, jEdit.getProperty( "net.sf.pmd.Copy/Paste_detection_can_not_be_performed_on_this_file\nbecause_the_mode_can_not_be_determined.", "Copy/Paste detection can not be performed on this file\nbecause the mode can not be determined." ), jEdit.getProperty( "net.sf.pmd.Copy/Paste_Detector", "Copy/Paste Detector" ), JOptionPane.INFORMATION_MESSAGE );
return;
}
instance.instanceCPDCurrentFile( view, view.getBuffer().getPath(), modeName );
}
public static void cpdCurrentFile( View view, VFSBrowser browser ) throws IOException {
instance.MainView=view;
VFSFile selectedFile[] = browser.getSelectedFiles();
if ( selectedFile == null || selectedFile.length == 0 ) {
JOptionPane.showMessageDialog( view, jEdit.getProperty( "net.sf.pmd.One_file_must_be_selected", "One file must be selected" ), NAME, JOptionPane.ERROR_MESSAGE );
return;
}
if ( selectedFile[0].getType() == VFSFile.DIRECTORY ) {
JOptionPane.showMessageDialog( view, jEdit.getProperty( "net.sf.pmd.Selected_file_cannot_be_a_Directory.", "Selected file cannot be a Directory." ), NAME, JOptionPane.ERROR_MESSAGE );
return;
}
setcolor(selectedFile);
String path = selectedFile[0].getPath();
instance.instanceCPDCurrentFile( view, path, getFileType( path ) );
}
public static void setcolor(VFSFile [] files){
JLabel label = new JLabel();
for (VFSFile vfs : files){
label.setText(String.format("<html><font color='blue'>%s</font></html>",
vfs.getName()));
}
}
// TODO: Replace this method with a smart file type/mode detector.
private static String getFileType( String name ) {
if ( name != null ) {
for ( String lang : LanguageFactory.supportedLanguages ) {
if ( name.endsWith( lang ) ) {
return lang;
}
}
if ( name.endsWith( "h" ) || name.endsWith( "cxx" ) || name.endsWith( "c++" ) ) {
return "cpp";
}
}
return name;
}
private void instanceCPDCurrentFile( View view, String filename, String fileType ) throws IOException {
CPD cpd = getCPD( fileType );
// Log.log(Log.DEBUG, PMDJEditPlugin.class , "See mode " + view.getBuffer().getMode().getName());
if ( cpd != null ) {
cpd.add( new File( filename ) );
cpd.go();
instance.processDuplicates( cpd, view );
//view.getDockableWindowManager().showDockableWindow( "cpd-viewer" );
view.getDockableWindowManager().hideDockableWindow("cpd-viewer");
} else {
view.getStatus().setMessageAndClear( jEdit.getProperty( "net.sf.pmd.CPD_does_not_yet_support_this_file_type>_", "CPD does not yet support this file type: " ) + fileType );
return;
}
}
/***
* Creates and set JFileChooser
* @return JFileChooser
*/
public static JFileChooser setChooser(){
JFileChooser chooser = new JFileChooser( jEdit.getProperty( LAST_DIRECTORY ) );
chooser.setFileSelectionMode( JFileChooser.DIRECTORIES_ONLY );
return chooser;
}
/***
* Creates and set JPanel pnlAccesory
* @return JPanel
*/
public static JPanel pnlaccessory(){
JPanel pnlAccessory = new JPanel();
pnlAccessory.setLayout( new KappaLayout() );
return pnlAccessory;
}
public static JTextField txtTitleSize(){
JTextField txttilesize = new JTextField(3 );
txttilesize.setText( jEdit.getIntegerProperty( DEFAULT_TILE_MINSIZE_PROPERTY, 100 ) + "" );
return txttilesize;
}
public static JComboBox filetypesel(CPDFileFilter [] choices){
JComboBox fileTypeSelector = new JComboBox( choices );
fileTypeSelector.setSelectedIndex( lastSelectedFilter );
fileTypeSelector.setEditable( false );
return fileTypeSelector;
}
public static void cpdDir( View view ) {
instance.MainView=view;
JFileChooser chooser = setChooser();
JPanel pnlAccessory = pnlaccessory();
JLabel lblMinTileSize = new JLabel( jEdit.getProperty( "net.sf.pmd.Minimum_Tile_size_>", "Minimum Tile size :" ) );
JTextField txttilesize = txtTitleSize();
JCheckBox chkRecursive = new JCheckBox( jEdit.getProperty( "net.sf.pmd.Recursive", "Recursive" ), jEdit.getBooleanProperty( CHECK_DIR_RECURSIVE ) );
CPDFileFilter[] choices = getCPDFileFilters();
JComboBox fileTypeSelector = filetypesel(choices);
JTextField inclusionsRegex = new JTextField();
inclusionsRegex.setText( lastInclusion );
JTextField exclusionsRegex = new JTextField();
exclusionsRegex.setText( lastExclusion );
pnlAccessory.add( lblMinTileSize, "0, 0, 1, 1, W,, 3" );
pnlAccessory.add( txttilesize, "1, 0, 1, 1, W,, 3" );
pnlAccessory.add( chkRecursive, "0, 1, 2, 1, W,, 3" );
pnlAccessory.add( fileTypeSelector, "0, 2, 2, 1, W,, 3" );
pnlAccessory.add( new JLabel( jEdit.getProperty( "net.sf.pmd.Inclusions", "Inclusions" ) ), "0, 3, 1, 1, W,, 3" );
pnlAccessory.add( inclusionsRegex, "1, 3, 1, 1, W, w, 3" );
pnlAccessory.add( new JLabel( jEdit.getProperty( "net.sf.pmd.Exclusions", "Exclusions" ) ), "0, 4, 1, 1, W,, 3" );
pnlAccessory.add( exclusionsRegex, "1, 4, 1, 1, W, w, 3" );
chooser.setAccessory( pnlAccessory );
int returnVal = chooser.showOpenDialog( view );
File selectedFile = null;
String inclusions = null;
String exclusions = null;
CPDFileFilter mode = null;
if ( returnVal == JFileChooser.APPROVE_OPTION ) {
selectedFile = chooser.getSelectedFile();
inclusions = inclusionsRegex.getText();
jEdit.setProperty( LAST_INCLUSION_REGEX, inclusions );
lastInclusion = inclusions;
exclusions = exclusionsRegex.getText();
jEdit.setProperty( LAST_EXCLUSION_REGEX, exclusions );
lastExclusion = exclusions;
mode = ( CPDFileFilter ) fileTypeSelector.getSelectedItem();
lastSelectedFilter = fileTypeSelector.getSelectedIndex();
jEdit.setIntegerProperty( LAST_SELECTED_FILTER, lastSelectedFilter );
mode.setInclusions( inclusions );
mode.setExclusions( exclusions );
if ( ! selectedFile.isDirectory() ) {
JOptionPane.showMessageDialog( view, jEdit.getProperty( "net.sf.pmd.Selection_not_a_directory.", "Selection not a directory." ), NAME, JOptionPane.ERROR_MESSAGE );
return;
}
int tilesize = 100;
try {
tilesize = Integer.parseInt( txttilesize.getText() );
} catch ( NumberFormatException e ) {
// use the default.
tilesize = jEdit.getIntegerProperty( DEFAULT_TILE_MINSIZE_PROPERTY, 100 );
}
try {
jEdit.setBooleanProperty( CHECK_DIR_RECURSIVE, chkRecursive.isSelected() );
instance.instanceCPDDir( view, selectedFile.getCanonicalPath(), tilesize, chkRecursive.isSelected(), mode );
} catch ( IOException e ) {
Log.log( Log.ERROR, instance, "PMD ERROR: Unable to open file " + selectedFile, e );
}
}
}
/**
* Run CPD on a directory selected from the File System Browser.
* @param view The current view.
* @param browser The file system browser supplying the user selecte directory to check.
* @param recursive If true, check all files in the selected directory and all child
* directories, otherwise, just check the files in the selected directory only.
*/
public static void cpdDir( View view, VFSBrowser browser, boolean recursive ) throws IOException {
instance.MainView=view;
if ( view != null && browser != null ) {
VFSFile selectedDir[] = browser.getSelectedFiles();
if ( selectedDir == null || selectedDir.length == 0 ) {
JOptionPane.showMessageDialog( view, jEdit.getProperty( "net.sf.pmd.One_Directory_has_to_be_selected_in_which_to_detect_duplicate_code.", "One Directory has to be selected in which to detect duplicate code." ), NAME, JOptionPane.ERROR_MESSAGE );
return;
}
setcolor(selectedDir);
if ( selectedDir[0].getType() != VFSFile.DIRECTORY ) {
JOptionPane.showMessageDialog( view, jEdit.getProperty( "net.sf.pmd.Selected_file_must_be_a_Directory.", "Selected file must be a Directory." ), NAME, JOptionPane.ERROR_MESSAGE );
return;
}
// display chooser for type of files to check
CPDFileFilter[] choices = getCPDFileFilters();
CPDFileFilter choice = ( CPDFileFilter ) JOptionPane.showInputDialog( view, jEdit.getProperty( "net.sf.pmd.Select_type_of_files_to_check>", "Select type of files to check:" ), jEdit.getProperty( "net.sf.pmd.CPD,_Select_File_Type", "CPD, Select File Type" ), JOptionPane.OK_CANCEL_OPTION, null, choices, choices[0] );
if ( choice == null ) {
return;
}
instance.instanceCPDDir( view, selectedDir[0].getPath(), jEdit.getIntegerProperty( DEFAULT_TILE_MINSIZE_PROPERTY, 100 ), recursive, choice );
}
}
private static CPDFileFilter[] getCPDFileFilters() {
List<CPDFileFilter> filters = new ArrayList <CPDFileFilter>();
Mode[] modes = jEdit.getModes();
for ( Mode mode : modes ) {
String filenameGlob = ( String ) mode.getProperty( "filenameGlob" );
if ( filenameGlob == null ) {
continue;
}
String modeName = mode.getName();
filenameGlob = filenameGlob.replaceAll( "[*.{}\\[\\]]", "" );
String[] extensions = filenameGlob.split( "[,]" );
filters.add( new CPDFileFilter( modeName, modeName, extensions ) );
}
Collections.sort( filters );
return filters.toArray( new CPDFileFilter[filters.size()] );
}
private void instanceCPDDir( final View view, final String dir, final int tileSize, final boolean recursive, final CPDFileFilter mode ) {
if ( dir != null ) {
SwingWorker<CPD, Object> worker = new SwingWorker<CPD, Object>() {
@Override
public CPD doInBackground() {
jEdit.setProperty( LAST_DIRECTORY, dir );
DefaultErrorSource errorSource = getErrorSource( view );
errorSource.clear();
CPD cpd = getCPD( tileSize, mode );
try {
if ( recursive ) {
cpd.addRecursively( dir );
} else {
cpd.addAllInDirectory( dir );
}
cpd.go();
return cpd;
} catch ( Exception e ) {
Log.log( Log.ERROR, this, "Unable to run CPD: " + e.getMessage() );
return null;
}
}
@Override
public void done() {
try {
CPD cpd = get();
if ( cpd == null ) {
view.getStatus().setMessageAndClear( jEdit.getProperty( "net.sf.pmd.Cannot_run_CPD_on_Invalid_directory/files.", "Cannot run CPD on Invalid directory/files." ) );
return;
}
instance.processDuplicates( cpd, view );
} catch ( Exception e ) {
Log.log( Log.ERROR, this, "CPD, nable to process duplicates: " + e.getMessage() );
}
}
};
worker.execute();
}
}
private void processDuplicates( CPD cpd, View view ) {
CPDDuplicateCodeViewer dv = getCPDDuplicateCodeViewer( view );
dv.clearDuplicates();
Iterator<Match> matches = cpd.getMatches();
if ( matches.hasNext() ) {
while ( matches.hasNext() ) {
Match match = matches.next();
CPDDuplicateCodeViewer.Duplicates duplicates = dv. new Duplicates( match.getLineCount() + " duplicate lines", match.getSourceCodeSlice() ); // NOPMD
for ( Iterator<TokenEntry> occurrences = match.iterator(); occurrences.hasNext(); ) {
TokenEntry mark = occurrences.next();
int lastLine = mark.getBeginLine() + match.getLineCount();
CPDDuplicateCodeViewer.Duplicate duplicate = dv. new Duplicate( mark.getTokenSrcID(), mark.getBeginLine(), lastLine ); // NOPMD
duplicates.addDuplicate( duplicate );
}
dv.addDuplicates( duplicates );
}
} else {
dv.getRoot().add( new DefaultMutableTreeNode( jEdit.getProperty( "net.sf.pmd.No_duplicates_found.", "No duplicates found." ), false ) );
}
dv.refreshTree();
dv.expandAll();
}
public CPDDuplicateCodeViewer getCPDDuplicateCodeViewer( View view ) {
view.getDockableWindowManager().showDockableWindow( "cpd-viewer" );
return ( CPDDuplicateCodeViewer ) view.getDockableWindowManager().getDockableWindow( "cpd-viewer" );
}
public static void checkFile( View view, VFSBrowser browser ) {
instance.checkFile( view, browser.getSelectedFiles() );
}
public void checkFile( View view, VFSFile de[] ) {
if ( view != null && de != null ) {
setcolor(de);
List<File> files = new ArrayList <File>();
for ( VFSFile file : de ) {
if ( file.getType() == VFSFile.FILE ) {
files.add( new File( file.getPath() ) );
}
}
process( files, view );
}
}
public static void checkDirectory( View view, VFSBrowser browser, boolean recursive ) {
VFSFile de[] = browser.getSelectedFiles();
if ( de == null || de.length == 0 || de[0].getType() != VFSFile.DIRECTORY ) {
JOptionPane.showMessageDialog( view, jEdit.getProperty( "net.sf.pmd.Selection_must_be_a_directory", "Selection must be a directory" ), NAME, JOptionPane.ERROR_MESSAGE );
return;
}
instance.process( instance.findFiles( de[0].getPath(), recursive ), view );
}
public void startProgressBarDisplay( final View view, final int max ) {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
instance.progressBar = getProgressWidget( view );
if ( instance.progressBar != null ) {
instance.progressBar.setMaximum( max );
instance.progressBar.setVisible(true);
}
}
} );
}
public void endProgressBarDisplay() {
if ( instance.progressBar != null ) {
int delay = 2000;
ActionListener timerTask = new ActionListener() {
public void actionPerformed( ActionEvent ae ) {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
instance.progressBar.complete();
}
} );
}
};
javax.swing.Timer timer = new javax.swing.Timer(delay, timerTask);
timer.setRepeats(false);
timer.start();
}
}
private PMDProgressWidgetFactory.ProgressWidget getProgressWidget( View view ) {
PMDProgressWidgetFactory widgetFactory = ( PMDProgressWidgetFactory ) ServiceManager.getService( "org.gjt.sp.jedit.gui.statusbar.StatusWidgetFactory", "pmd" );
if ( widgetFactory == null ) {
return null;
}
return ( PMDProgressWidgetFactory.ProgressWidget ) widgetFactory.getWidget( view );
}
/**
* Removes the PMD progress bar from all Views.
*/
public static void removeProgressBar() {
String statusBar = jEdit.getProperty("view.status");
String[] widgets = statusBar.split("[ ]");
StringBuilder sb = new StringBuilder();
for (String widget : widgets) {
if ("pmd".equals(widget)) {
continue;
}
sb.append(widget).append(' ');
}
sb.deleteCharAt(sb.length() - 1);
jEdit.setProperty("view.status", sb.toString());
}
/**
* Adds the PMD progress bar to all views depending on the SHOW_PROGRESS property value.
*/
public static void addProgressBar() {
removeProgressBar();
StringBuilder statusBar = new StringBuilder(jEdit.getProperty("view.status"));
boolean showProgressBar = jEdit.getBooleanProperty(PMDJEditPlugin.SHOW_PROGRESS, false);
if (showProgressBar) {
statusBar.append(" pmd");
}
jEdit.setProperty("view.status", statusBar.toString());
}
public void exportErrorAsReport( final View view, Report reports[] ) {
String format = jEdit.getProperty( PMDJEditPlugin.RENDERER );
// "None", "Text", "Html", "XML", "CSV"
if ( format != null && ! format.equals( "None" ) ) {
net.sourceforge.pmd.renderers.Renderer renderer = null;
if ( "XML".equals( format ) ) {
renderer = new XMLRenderer();
} else {
if ( "Html".equals( format ) ) {
renderer = new HTMLRenderer();
} else {
if ( "CSV".equals( format ) ) {
renderer = new CSVRenderer();
} else {
if ( "Text".equals( format ) ) {
renderer = new TextRenderer();
} else {
JOptionPane.showMessageDialog( view, jEdit.getProperty( "net.sf.pmd.Invalid_Renderer", "Invalid Renderer" ), NAME, JOptionPane.ERROR_MESSAGE );
return;
}
}
}
}
if ( reports != null ) {
final StringWriter sw = new StringWriter();
final Writer writer = new BufferedWriter( sw );
try {
renderer.setWriter( writer );
renderer.start();
for ( Report report : reports ) {
if ( report != null ) {
try {
renderer.renderFileReport( report ); // TODO: pmd throws an NPE here
} catch ( Exception e ) { // NOPMD
}
}
}
renderer.end();
} catch ( IOException ioe ) {
Log.log( Log.ERROR, this, "Renderer can't report.", ioe );
} finally {
try {
writer.close();
} catch ( Exception ignored ) { // NOPMD
}
}
final Buffer buffer = jEdit.newFile( view );
view.setBuffer( buffer );
SwingUtilities.invokeLater( new Runnable() {
public void run() {
buffer.writeLock();
buffer.insert(0, sw.toString() );
buffer.writeUnlock();
}
} );
}
}
}
class ProgressBar extends JPanel {
private JProgressBar pBar;
private View view;
public ProgressBar( View view, int min, int max ) {
super();
this.view = view;
setLayout( new BorderLayout() );
pBar = new JProgressBar( min, max );
pBar.setBorder( new EtchedBorder( EtchedBorder.RAISED ) );
pBar.setToolTipText( jEdit.getProperty( "net.sf.pmd.PMD_Check_in_Progress", "PMD Check in Progress" ) );
pBar.setForeground( jEdit.getColorProperty( "pmd.progressbar.background" ) );
pBar.setBackground( jEdit.getColorProperty( "view.status.background" ) );
pBar.setStringPainted( true );
//add( pBar, BorderLayout.CENTER );
MainView.getContentPane().add(BorderLayout.WEST, pBar);
}
public void increment( final int num ) {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
pBar.setValue( pBar.getValue() + num );
}
} );
}
public void completeBar() {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
pBar.setValue( pBar.getMaximum() );
view.getStatus().remove( pBar );
}
} );
}
}
private CPD getCPD( int tileSize, final CPDFileFilter fileType ) {
Language lang = null;
LanguageFactory lf = new LanguageFactory();
List<String> supportedLanguages = Arrays.asList( LanguageFactory.supportedLanguages );
Properties props = new Properties();
if ( "java".equals( fileType.getMode() ) ) {
props.setProperty( JavaTokenizer.IGNORE_LITERALS, String.valueOf( jEdit.getBooleanProperty( PMDJEditPlugin.IGNORE_LITERALS ) ) );
}
if ( supportedLanguages.contains( fileType.getMode() ) ) {
lang = lf.createLanguage( fileType.getMode(), props );
} else {
lang = new AnyLanguage( fileType.getExtensions() ) {
public FilenameFilter getFileFilter() {
return fileType;
}
};
}
return lang == null ? null : new CPD( new CPDConfiguration( tileSize, lang, "UTF-8" ) );
}
private CPD getCPD( String fileType ) {
Mode mode = jEdit.getMode( fileType );
if ( mode == null ) {
return null;
}
String filenameGlob = ( String ) mode.getProperty( "filenameGlob" );
if ( filenameGlob == null ) {
return null;
}
filenameGlob = filenameGlob.replaceAll( "[*.{}]", "" );
String[] extensions = filenameGlob.split( "[,]" );
String modeName = mode.getName();
CPDFileFilter filter = new CPDFileFilter( modeName, modeName, extensions );
return getCPD( jEdit.getIntegerProperty( PMDJEditPlugin.DEFAULT_TILE_MINSIZE_PROPERTY, 100 ), filter );
}
/**
* Run the PMD rule designer.
*/
public static void runDesigner() {
String[] args = new String [] {"-noexitonclose"};
new Designer( args );
}
}