/* * Created on 9 mai 2005 * * Copyright (c) 2005, PMD for Eclipse Development Team * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The end-user documentation included with the redistribution, if * any, must include the following acknowledgement: * "This product includes software developed in part by support from * the Defense Advanced Research Project Agency (DARPA)" * * Neither the name of "PMD for Eclipse Development Team" nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package net.sourceforge.pmd.eclipse.ui.views; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import net.sourceforge.pmd.eclipse.ui.model.AbstractPMDRecord; import net.sourceforge.pmd.eclipse.ui.model.FileRecord; import net.sourceforge.pmd.eclipse.ui.model.FileToMarkerRecord; import net.sourceforge.pmd.eclipse.ui.model.MarkerRecord; import net.sourceforge.pmd.eclipse.ui.model.PackageRecord; import net.sourceforge.pmd.eclipse.ui.model.FolderRecord; import net.sourceforge.pmd.eclipse.ui.model.ProjectRecord; import net.sourceforge.pmd.eclipse.ui.model.RootRecord; import net.sourceforge.pmd.eclipse.util.Util; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; /** * Provides the Violation Overview with Content Elements can be * PackageRecords or FileRecords * * @author SebastianRaffel ( 09.05.2005 ), Philppe Herlin, Sven Jacob * */ public class ViolationOverviewContentProvider implements ITreeContentProvider, IStructuredContentProvider, IResourceChangeListener { private static final Log LOG = LogFactory.getLog(ViolationOverviewContentProvider.class); protected boolean filterPackages; private final ViolationOverview violationView; private TreeViewer treeViewer; private RootRecord root; private ChangeEvaluator changeEvaluator; /** * Constructor * * @param view */ public ViolationOverviewContentProvider(ViolationOverview view) { super(); violationView = view; treeViewer = view.getViewer(); } /** * @see org.eclipse.jface.viewers.IContentProvider#dispose() */ public void dispose() { if (root != null) { IWorkspaceRoot workspaceRoot = (IWorkspaceRoot) root.getResource(); workspaceRoot.getWorkspace().removeResourceChangeListener(this); } } /** * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object) */ public Object[] getElements(Object inputElement) { return getChildren(inputElement); } /** * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object) */ public Object[] getChildren(Object parentElement) { if (parentElement instanceof IWorkspaceRoot || parentElement instanceof RootRecord) { return getChildrenOfRoot(); } else if (parentElement instanceof PackageRecord) { return getChildrenOfPackage((PackageRecord)parentElement); } else if (parentElement instanceof FolderRecord) { return getChildrenOfFolder((FolderRecord)parentElement); } else if (parentElement instanceof FileRecord) { return getChildrenOfFile((FileRecord)parentElement); } else if (parentElement instanceof MarkerRecord) { return getChildrenOfMarker((MarkerRecord)parentElement); } return Util.EMPTY_ARRAY; } /** * Gets the children of a file record. * @param record FileRecord * @return children as array */ private Object[] getChildrenOfFile(FileRecord record) { return record.getChildren(); } /** * Gets the children of a marker record. * @param record MarkerRecord * @return children as array */ private Object[] getChildrenOfMarker(MarkerRecord record) { record.updateChildren(); return record.getChildren(); } /** * Gets the children of a PackageRecord. * If the presentation type is {@link ViolationOverview#SHOW_MARKERS_FILES} the children (MarkerRecord) * of the children (FileRecord) will be get. * * @param record PackageRecord * @return children as array */ private Object[] getChildrenOfPackage(PackageRecord record) { return getChildrenOfPackageOrFolder(record); } private Object[] getChildrenOfFolder(FolderRecord record) { return getChildrenOfPackageOrFolder(record); } private Object[] getChildrenOfPackageOrFolder(AbstractPMDRecord record) { if (violationView.getShowType() == ViolationOverview.SHOW_MARKERS_FILES) { Map<String, AbstractPMDRecord> markers = new HashMap<String, AbstractPMDRecord>(); List<AbstractPMDRecord> files = record.getChildrenAsList(); for (AbstractPMDRecord fileRec : files) { List<AbstractPMDRecord> newMarkers = fileRec.getChildrenAsList(); for (AbstractPMDRecord markerRec : newMarkers) { markers.put(markerRec.getName(), markerRec); } } return markers.values().toArray(new MarkerRecord[markers.size()]); } else { return record.getChildren(); } } /** * Gets the children of the root depending on the show type. * @return children */ private Object[] getChildrenOfRoot() { // ... we care about its Project's List<AbstractPMDRecord> projects = root.getChildrenAsList(); ProjectRecord[] projectArray = new ProjectRecord[projects.size()]; projects.toArray(projectArray); // we make a List of all Packages List<AbstractPMDRecord> packages = new ArrayList<AbstractPMDRecord>(); for (ProjectRecord element : projectArray) { if (element.isProjectOpen()) { packages.addAll(element.getChildrenAsList()); } } switch (violationView.getShowType()) { case ViolationOverview.SHOW_MARKERS_FILES: case ViolationOverview.SHOW_PACKAGES_FILES_MARKERS: // show packages return packages.toArray(); case ViolationOverview.SHOW_FILES_MARKERS: // show files List<AbstractPMDRecord> files = new ArrayList<AbstractPMDRecord>(); for (AbstractPMDRecord packageRec : packages) { files.addAll(packageRec.getChildrenAsList()); } return files.toArray(); default: // do nothing } return Util.EMPTY_ARRAY; } /** * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object) */ public Object getParent(Object element) { Object parent = null; AbstractPMDRecord record = (AbstractPMDRecord) element; switch (violationView.getShowType()) { case ViolationOverview.SHOW_FILES_MARKERS: if (element instanceof FileRecord) { parent = root; } else { parent = record.getParent(); } break; case ViolationOverview.SHOW_MARKERS_FILES: if (element instanceof FileToMarkerRecord) { parent = record.getParent(); } else if (element instanceof PackageRecord) { parent = root; } else if (element instanceof MarkerRecord) { parent = record.getParent().getParent(); } break; case ViolationOverview.SHOW_PACKAGES_FILES_MARKERS: if (element instanceof PackageRecord) { parent = root; } else { parent = record.getParent(); } break; default: // do nothing } return parent; } /** * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object) */ public boolean hasChildren(Object element) { boolean hasChildren = true; // find out if this is the last level in the tree (to avaoid recursion) switch (violationView.getShowType()) { case ViolationOverview.SHOW_PACKAGES_FILES_MARKERS: case ViolationOverview.SHOW_FILES_MARKERS: hasChildren ^= element instanceof MarkerRecord; break; case ViolationOverview.SHOW_MARKERS_FILES: hasChildren ^= element instanceof FileToMarkerRecord; break; default: // do nothing } if (hasChildren) { hasChildren = getChildren(element).length > 0; } return hasChildren; } /** * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, * java.lang.Object, java.lang.Object) */ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { LOG.debug("ViolationOverview inputChanged"); treeViewer = (TreeViewer) viewer; // this is called, when the View is instantiated and gets Input // or if the Source of Input changes // we remove an existing ResourceChangeListener IWorkspaceRoot workspaceRoot; if (root != null) { LOG.debug("remove current listener"); workspaceRoot = (IWorkspaceRoot) root.getResource(); workspaceRoot.getWorkspace().removeResourceChangeListener(this); } // ... to add a new one, so we can listen to Changes made // to Resources in the Workspace if (newInput instanceof IWorkspaceRoot) { LOG.debug("the new input is a workspace root"); // either we got a WorkspaceRoot workspaceRoot = (IWorkspaceRoot) newInput; root = new RootRecord(workspaceRoot); workspaceRoot.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE); } else if (newInput instanceof RootRecord) { LOG.debug("the new input is a root record"); // ... or already a Record for it root = (RootRecord) newInput; workspaceRoot = (IWorkspaceRoot) root.getResource(); workspaceRoot.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE); } changeEvaluator = new ChangeEvaluator(root); } /** * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent) */ public void resourceChanged(IResourceChangeEvent event) { final ChangeRecord<AbstractPMDRecord> changes = changeEvaluator.changeRecordFor(event); // the additions, removals and changes are given to the viewer so that it can update itself // updating the table MUST be in sync treeViewer.getControl().getDisplay().syncExec(new Runnable() { public void run() { updateViewer(changes); } }); } // public void resourceChanged(IResourceChangeEvent event) { // // LOG.debug("resource changed event"); // // List<IMarkerDelta> markerDeltas = MarkerUtil.markerDeltasIn(event); // // // first we get a List of changes to Files and Projects // // so we won't need updating everything // List<IResource> changedFiles = new ArrayList<IResource>(); // List<IProject> changedProjects = new ArrayList<IProject>(); // for (IMarkerDelta markerDelta : markerDeltas) { // IResource resource = markerDelta.getResource(); // IProject project = resource.getProject(); // // // the lists should not contain Projects or Resources twice // // if (!changedFiles.contains(resource)) { // changedFiles.add(resource); // LOG.debug("Resource " + resource.getName() + " has changed"); // } // // if (!changedProjects.contains(project)) { // changedProjects.add(project); // LOG.debug("Project " + project.getName() + " has changed"); // } // } // // // we can add, change or remove Resources // // all this changes are given to the viewer later // final List<AbstractPMDRecord> additions = new ArrayList<AbstractPMDRecord>(); // final List<AbstractPMDRecord> removals = new ArrayList<AbstractPMDRecord>(); // final List<AbstractPMDRecord> changes = new ArrayList<AbstractPMDRecord>(); // // // we go through the changed Projects // for (IProject project : changedProjects) { // LOG.debug("Processing changes for project " + project.getName()); // ProjectRecord projectRec = (ProjectRecord) root.findResource(project); // // // if the Project is closed or deleted, // // we also delete it from the Model and go on // if (!(project.isOpen() && project.isAccessible())) { // NOPMD by Sven on 09.11.06 22:17 // LOG.debug("The project is not open or not accessible. Remove it"); // List<AbstractPMDRecord>[] array = updateFiles(project, changedFiles); // removals.addAll(array[1]); // root.removeResource(project); // } // // // if we couldn't find the Project // // then it has to be new // else if (projectRec == null) { // LOG.debug("Cannot find a project record for it. Add it."); // projectRec = (ProjectRecord) root.addResource(project); // } // // // then we can update the Files for the new or updated Project // List<AbstractPMDRecord>[] array = updateFiles(project, changedFiles); // additions.addAll(array[0]); // removals.addAll(array[1]); // changes.addAll(array[2]); // } // // // the additions, removals and changes are given to the viewer // // so that it can update itself // // updating the table MUST be in sync // treeViewer.getControl().getDisplay().syncExec(new Runnable() { // public void run() { // updateViewer(additions, removals, changes); // } // }); // } /** * Updates the Files for a given Project * * @param project * @param changedFiles, a List of all changed Files * @return an List of Lists containing additions [0], removals [1] * and changes [2] (Array-Position in Brackets) */ // protected List<AbstractPMDRecord>[] updateFiles(IProject project, List<IResource> changedFiles) { // // final List<AbstractPMDRecord> additions = new ArrayList<AbstractPMDRecord>(); // final List<AbstractPMDRecord> removals = new ArrayList<AbstractPMDRecord>(); // final List<AbstractPMDRecord> changes = new ArrayList<AbstractPMDRecord>(); // List<AbstractPMDRecord>[] updatedFiles = new List[] { additions, removals, changes }; // // // we search for the ProjectRecord to the Project // // if it doesn't exist, we return nothing // final ProjectRecord projectRec = (ProjectRecord) root.findResource(project); // // // we got through all files // if (projectRec != null && project.isAccessible()) { // updatedFiles = ChangeEvaluator.searchProjectForModifications(projectRec, changedFiles); // } // // // if the project is deleted or closed // else if (projectRec != null) { // final List<AbstractPMDRecord> packages = projectRec.getChildrenAsList(); // // ... we add all Packages to the removals // // so they are not shown anymore // removals.addAll(packages); // for (int k = 0; k < packages.size(); k++) { // final PackageRecord packageRec = (PackageRecord) packages.get(k); // removals.addAll(packageRec.getChildrenAsList()); // } // updatedFiles = new List[] { additions, removals, changes }; // } // // return updatedFiles; // } // /** // * Analyzes the modification inside a single project and compute the list of additions, updates and removals. // * // * @param projectRec // * @param changedFiles // * @return // */ // private List<AbstractPMDRecord>[] searchProjectForModifications(ProjectRecord projectRec, List<IResource> changedFiles) { // final List<AbstractPMDRecord> additions = new ArrayList<AbstractPMDRecord>(); // final List<AbstractPMDRecord> removals = new ArrayList<AbstractPMDRecord>(); // final List<AbstractPMDRecord> changes = new ArrayList<AbstractPMDRecord>(); // final IProject project = (IProject) projectRec.getResource(); // // LOG.debug("Analyses project " + project.getName()); // // for (IResource resource : changedFiles) { // LOG.debug("Analyses resource " + resource.getName()); // // // ... and first check, if the project is the right one // if (project.equals(resource.getProject())) { // final AbstractPMDRecord rec = projectRec.findResource(resource); // if (rec != null && rec.getResourceType() == IResource.FILE) { // final FileRecord fileRec = (FileRecord) rec; // fileRec.updateChildren(); // if (fileRec.getResource().isAccessible() && fileRec.hasMarkers()) { // LOG.debug("The file has changed"); // changes.add(fileRec); // } else { // LOG.debug("The file has been removed"); // projectRec.removeResource(fileRec.getResource()); // removals.add(fileRec); // // // remove parent if no more markers // final PackageRecord packageRec = (PackageRecord) fileRec.getParent(); // if (!packageRec.hasMarkers()) { // projectRec.removeResource(fileRec.getParent().getResource()); // removals.add(packageRec); // } // } // } else if (rec == null) { // LOG.debug("This is a new file."); // final AbstractPMDRecord fileRec = projectRec.addResource(resource); // additions.add(fileRec); // } else { // LOG.debug("The resource found is not a file! type found : " + rec.getResourceType()); // } // } else { // LOG.debug("The project resource is not the same! (" + resource.getProject().getName() + ')'); // } // } // // return new List[] { additions, removals, changes }; // } /** * Applies found updates on the table, adapted from Philippe Herlin * * @param additions * @param removals * @param changes */ // protected void updateViewer(List<AbstractPMDRecord> additions, List<AbstractPMDRecord> removals, List<AbstractPMDRecord> changes) { // // // perform removals // if (removals.size() > 0) { // treeViewer.cancelEditing(); // treeViewer.remove(removals.toArray()); // } // // // perform additions // if (additions.size() > 0) { // for (int i = 0; i < additions.size(); i++) { // final AbstractPMDRecord addedRec = additions.get(i); // if (addedRec instanceof FileRecord) { // treeViewer.add(addedRec.getParent(), addedRec); // } else { // treeViewer.add(root, addedRec); // } // } // } // // // perform changes // if (changes.size() > 0) { // treeViewer.update(changes.toArray(), null); // } // // violationView.refresh(); // } protected void updateViewer(ChangeRecord<AbstractPMDRecord> changes) { // perform removals if (changes.hasRemovals()) { treeViewer.cancelEditing(); treeViewer.remove(changes.removals.toArray()); } // perform additions (if any) for (AbstractPMDRecord addedRec : changes.additions) { if (addedRec instanceof FileRecord) { treeViewer.add(addedRec.getParent(), addedRec); } else { treeViewer.add(root, addedRec); } } // perform changes if (changes.hasChanges()) { treeViewer.update(changes.changes.toArray(), null); } violationView.refresh(); } }