Java tutorial
/******************************************************************************* * Copyright (c) 2016 KDM Analytics, Inc. All rights reserved. This program and * the accompanying materials are made available under the terms of the Open * Source Initiative OSI - Open Software License v3.0 which accompanies this * distribution, and is available at * http://www.opensource.org/licenses/osl-3.0.php/ ******************************************************************************/ package com.kdmanalytics.toif.ui.views; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.widgets.Display; import com.kdmanalytics.toif.ui.common.FindingEntry; import com.kdmanalytics.toif.ui.common.FindingGroup; import com.kdmanalytics.toif.ui.common.IFindingEntry; /** * Provides content to the view. Beyond working like a simple content provider, this class provides * methods for updating a subset of the data, reducing the amount of work required to do a refresh. * * The findings are mapped by IFile, which makes it easy to find and update findings on a file by * file basis. * * @author Ken Duck * */ class FindingContentProvider implements ITreeContentProvider { /** * A map of entries to files. This makes it easier for us to update * entries on a per-file basis. */ private Map<IFile, List<IFindingEntry>> findings = new HashMap<IFile, List<IFindingEntry>>(); /** * Project we are currently showing issues for */ private IProject currentProject; public void inputChanged(Viewer v, Object oldInput, Object newInput) { // If a new project is selected, clear the old findings to force a reload. if (newInput instanceof IProject) { if (newInput != currentProject) { currentProject = (IProject) newInput; findings.clear(); } } } /* * (non-Javadoc) * @see org.eclipse.jface.viewers.IContentProvider#dispose() */ public void dispose() { } /* * (non-Javadoc) * @see org.eclipse.jface.viewers.ITreeContentProvider#getElements(java.lang.Object) */ public Object[] getElements(Object parent) { return getChildren(parent); } /** Update the findings in the findings map for all IFiles found * within the specified resource. * * Also returns a list of new findings. * * @param workspaceRoot * @return A list of new findings */ private List<FindingEntry> updateFindings(IResource resource) { List<FindingEntry> results = new LinkedList<FindingEntry>(); if (resource.exists()) { if (resource.getProject().isOpen()) { try { IMarker[] problems = resource.findMarkers(IMarker.TEXT, true, IResource.DEPTH_INFINITE); for (IMarker marker : problems) { String type = marker.getType(); if (type != null && type.startsWith("com.kdmanalytics.toif")) { FindingEntry entry = new FindingEntry(marker); results.add(entry); } } } catch (CoreException e) { e.printStackTrace(); } } } return results; } /** Return an array of all of the entries. * * @return */ public IFindingEntry[] getEntries() { List<IFindingEntry> results = new LinkedList<IFindingEntry>(); // Force update if the findings are currently empty if (findings.isEmpty() && currentProject != null) { updateFindings(currentProject); } for (List<IFindingEntry> list : findings.values()) { for (IFindingEntry entry : list) { results.add(entry); } } return results.toArray(new IFindingEntry[results.size()]); } /** Add an entry to the map * * @param entry */ private void addEntry(FindingEntry entry) { IFile file = entry.getFile(); if (!findings.containsKey(file)) { findings.put(file, new LinkedList<IFindingEntry>()); } List<IFindingEntry> list = findings.get(file); boolean grouped = false; for (Iterator<IFindingEntry> it = list.iterator(); it.hasNext();) { IFindingEntry fe = it.next(); if (canGroup(fe, entry)) { grouped = true; if (fe instanceof FindingGroup) { ((FindingGroup) fe).add(entry); } else { it.remove(); FindingGroup group = new FindingGroup(fe.getFile(), fe.getLineNumber(), fe.getSfp(), fe.getCwe()); group.add((FindingEntry) fe); group.add(entry); list.add(0, group); } break; } } if (!grouped) { list.add(entry); } } /** * * @param e1 * @param e2 * @return */ private boolean canGroup(IFindingEntry e1, FindingEntry e2) { if (!e1.getFile().equals(e2.getFile())) return false; if (e1.getLineNumber() != e2.getLineNumber()) return false; if (!e1.getCwe().equals(e2.getCwe())) return false; return true; } /** Update the information for the specified resource. Tell the view about * the changes. * * @param resource */ public void update(final TreeViewer viewer, final IFile file) { if (file == null) return; if (file.getProject() != this.currentProject) return; // Ensure we are running on the UI thread Display.getDefault().syncExec(new Runnable() { public void run() { // Get old findings List<IFindingEntry> oldFindings = findings.get(file); if (oldFindings == null) oldFindings = new LinkedList<IFindingEntry>(); // Get new findings List<FindingEntry> newFindings = updateFindings(file); if (!equals(oldFindings, newFindings)) { findings.remove(file); if (oldFindings != null) { for (IFindingEntry finding : oldFindings) { viewer.remove(finding); } } viewer.refresh(); for (FindingEntry finding : newFindings) { addEntry(finding); } } } /** Determine if lists are equal by sorting them and then comparing * them. * * @param list1 * @param newFindings * @return */ private boolean equals(List<IFindingEntry> list1, List<FindingEntry> newFindings) { if (list1 == null && newFindings == null) return true; if (list1 == null) return false; if (newFindings == null) return false; List<FindingEntry> c1 = new LinkedList<FindingEntry>(); List<FindingEntry> c2 = new LinkedList<FindingEntry>(); for (IFindingEntry entry : list1) { if (entry instanceof FindingEntry) { c1.add((FindingEntry) entry); } else { Collection<IFindingEntry> children = ((FindingGroup) entry).getFindingEntries(); for (IFindingEntry child : children) { c1.add((FindingEntry) child); } } } for (IFindingEntry entry : newFindings) { if (entry instanceof FindingEntry) { c2.add((FindingEntry) entry); } else { Collection<IFindingEntry> children = ((FindingGroup) entry).getFindingEntries(); for (IFindingEntry child : children) { c2.add((FindingEntry) child); } } } if (c1.size() != c2.size()) return false; Collections.sort(c1); Collections.sort(c2); return c1.equals(c2); } }); } /** * Clear the data so we know to reload it. */ public void clear() { findings.clear(); } /* * (non-Javadoc) * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object) */ @Override public Object[] getChildren(Object parent) { if (parent instanceof FindingGroup) { Object[] children = ((FindingGroup) parent).getFindingEntryArray(); return children; } if (parent instanceof IProject) { // Make sure the findings are only fully instantiated *once* if (findings.isEmpty()) { if (currentProject != null && currentProject.isOpen()) { Collection<FindingEntry> findings = updateFindings(currentProject); if (findings != null) { for (FindingEntry finding : findings) { addEntry(finding); } } } } return getEntries(); } return new Object[0]; } @Override public Object getParent(Object element) { return null; } @Override public boolean hasChildren(Object element) { if (element instanceof FindingGroup) { return true; } return false; } /** Get all of the entries themselves * * @return */ public FindingEntry[] getFindingEntries() { IFindingEntry[] entries = getEntries(); List<FindingEntry> results = new LinkedList<FindingEntry>(); for (IFindingEntry entry : entries) { if (entry instanceof FindingEntry) { results.add((FindingEntry) entry); } else { Collection<IFindingEntry> children = ((FindingGroup) entry).getFindingEntries(); for (IFindingEntry child : children) { results.add((FindingEntry) child); } } } return results.toArray(new FindingEntry[results.size()]); } }