/*
* ==========================================================================%%#
* EasyPmd
* ===========================================================================%%
* Copyright (C) 2009 - 2016 Gianluca Costa
* ===========================================================================%%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* ==========================================================================%##
*/
package info.gianlucacosta.easypmd.ide;
import info.gianlucacosta.easypmd.ide.editor.AnnotationService;
import info.gianlucacosta.easypmd.ide.editor.GuardedSectionsAnalyzer;
import info.gianlucacosta.easypmd.ide.editor.ScanMessageAnnotationList;
import info.gianlucacosta.easypmd.ide.options.Options;
import info.gianlucacosta.easypmd.ide.options.OptionsService;
import info.gianlucacosta.easypmd.ide.tasklist.ScanMessageTaskList;
import info.gianlucacosta.easypmd.pmdscanner.PmdScanner;
import info.gianlucacosta.easypmd.pmdscanner.ScanMessageList;
import org.netbeans.spi.tasklist.FileTaskScanner;
import org.netbeans.spi.tasklist.Task;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.DataObject;
import org.openide.loaders.DataObjectNotFoundException;
import org.openide.util.NbBundle;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.util.List;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* The link between the IDE and the plugin's scan system
*/
public class IdeScanner extends FileTaskScanner {
private static final Logger logger = Logger.getLogger(IdeScanner.class.getName());
private static final String OPTIONS_PATH = "Advanced/info.gianlucacosta.easypmd";
private final AnnotationService annotationService;
private final DialogService dialogService;
private final OptionsService optionsService;
private Callback callback;
private PmdScanner pmdScanner;
private Options options;
private final Lock readOptionsLock;
private final Lock writeOptionsLock;
public IdeScanner(String displayName, String description, String optionsPath) {
super(displayName, description, optionsPath);
dialogService = Injector.lookup(DialogService.class);
annotationService = Injector.lookup(AnnotationService.class);
optionsService = Injector.lookup(OptionsService.class);
ReadWriteLock optionsLock = new ReentrantReadWriteLock();
readOptionsLock = optionsLock.readLock();
writeOptionsLock = optionsLock.writeLock();
options = optionsService.getOptions();
try {
pmdScanner = new PmdScanner(options);
} catch (RuntimeException ex) {
showScannerConfigurationException(ex);
}
optionsService.addOptionsChangedListener(() -> {
logger.log(Level.INFO, "The options have changed: reinitializing the IDE scanner");
writeOptionsLock.lock();
try {
annotationService.detachAllAnnotations();
options = optionsService.getOptions();
try {
pmdScanner = new PmdScanner(options);
} catch (RuntimeException ex) {
pmdScanner = null;
showScannerConfigurationException(ex);
}
if (callback != null) {
callback.refreshAll();
}
} finally {
writeOptionsLock.unlock();
}
});
}
private void showScannerConfigurationException(Exception ex) {
logger.log(Level.WARNING, "Configuration exception for EasyPmd:\n%s", ex);
dialogService.showWarning(String.format("Could not run EasyPmd because of configuration errors:\n\t%s (%s)", ex.getMessage(), ex.getClass().getSimpleName()));
}
@Override
public List<? extends Task> scan(FileObject fileObject) {
if (pmdScanner == null) {
return new ScanMessageTaskList();
}
readOptionsLock.lock();
try {
File file = FileUtil.toFile(fileObject);
if (file == null || !file.isFile()) {
return new ScanMessageTaskList();
}
annotationService.detachAnnotationsFrom(fileObject);
String filePath = file.getPath();
if (!options.getPathFilteringOptions().isPathValid(filePath)) {
return new ScanMessageTaskList();
}
DataObject dataObject;
try {
dataObject = DataObject.find(fileObject);
} catch (DataObjectNotFoundException ex) {
throw new RuntimeException(ex);
}
ScanMessageList scanMessages = pmdScanner.scanFile(file);
if (!options.isShowAllMessagesInGuardedSections()) {
GuardedSectionsAnalyzer guardedSectionsAnalyzer = new GuardedSectionsAnalyzer(dataObject);
Set<Integer> guardedPmdLineNumbers = guardedSectionsAnalyzer.getGuardedLineNumbers();
if (!guardedPmdLineNumbers.isEmpty()) {
scanMessages = scanMessages.filterOutGuardedSections(guardedPmdLineNumbers);
}
}
ScanMessageTaskList tasks = new ScanMessageTaskList(fileObject, scanMessages);
if (options.isShowAnnotationsInEditor()) {
ScanMessageAnnotationList annotations = new ScanMessageAnnotationList(scanMessages);
annotationService.attachAnnotationsTo(dataObject, annotations);
}
return tasks;
} finally {
readOptionsLock.unlock();
}
}
@Override
public void attach(Callback callback) {
logger.log(Level.INFO, String.format("Attaching callback: %s", callback));
this.callback = callback;
if (callback == null) {
annotationService.detachAllAnnotations();
} else {
callback.refreshAll();
}
}
public static IdeScanner create() throws ParserConfigurationException {
ResourceBundle bundle = NbBundle.getBundle(IdeScanner.class);
return new IdeScanner(
bundle.getString("Filter_DisplayName"),
bundle.getString("Filter_Description"),
OPTIONS_PATH);
}
}