From 8968a848387a0c29ca0bf64fc60b535e6dc4ceff Mon Sep 17 00:00:00 2001 From: Philip Graf Date: Sun, 1 Feb 2015 22:53:22 +0100 Subject: [PATCH] Mark invalid configurations in the PMD property page with an icon Invalid configurations are marked with a warning icon (when they are not activated) or an error icon (when they are activated). A tool tip shows the actual absolute path of the configuration to help the user resolve the problem. This is the second part of resolving issue #20. --- .../pmd/builder/LocationResolverTest.java | 260 ++++++++++++++---- .../PMDPropertyPageControllerTest.java | 16 +- .../ProjectModelSerializerTest.java | 4 +- .../pmd/v07tov08/V07ToV08ConverterTest.java | 2 +- .../eclipse/pmd/builder/LocationResolver.java | 95 +++++-- .../eclipse/pmd/cache/RuleSetsCache.java | 2 +- .../pmd/cache/RuleSetsCacheLoader.java | 2 +- .../eclipse/pmd/domain/LocationContext.java | 2 +- .../pmd/properties/LocationLabelProvider.java | 48 ++++ .../pmd/properties/NameLabelProvider.java | 53 ++++ .../properties/PMDPropertyPageController.java | 8 +- .../PMDPropertyPageModelTransformer.java | 49 ++-- .../properties/PMDPropertyPageViewModel.java | 30 +- .../RuleSetConfigurationLabelProvider.java | 64 +++++ .../properties/RuleSetConfigurationTable.java | 29 +- .../pmd/properties/TypeLabelProvider.java | 32 +++ .../ProjectConfigurationContentHandler.java | 2 +- .../RuleSetConfigurationToXMLTag.java | 2 +- .../pmd/v07tov08/V07ToV08Converter.java | 4 +- .../AddRuleSetConfigurationController.java | 2 +- .../wizard/AddRuleSetConfigurationModel.java | 4 +- 21 files changed, 572 insertions(+), 138 deletions(-) create mode 100644 ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/LocationLabelProvider.java create mode 100644 ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/NameLabelProvider.java create mode 100644 ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/RuleSetConfigurationLabelProvider.java create mode 100644 ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/TypeLabelProvider.java diff --git a/ch.acanda.eclipse.pmd.core.tests/src/ch/acanda/eclipse/pmd/builder/LocationResolverTest.java b/ch.acanda.eclipse.pmd.core.tests/src/ch/acanda/eclipse/pmd/builder/LocationResolverTest.java index 1e0a5bc5..a769e6e8 100644 --- a/ch.acanda.eclipse.pmd.core.tests/src/ch/acanda/eclipse/pmd/builder/LocationResolverTest.java +++ b/ch.acanda.eclipse.pmd.core.tests/src/ch/acanda/eclipse/pmd/builder/LocationResolverTest.java @@ -17,8 +17,11 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import org.eclipse.core.resources.IProject; @@ -39,103 +42,188 @@ public class LocationResolverTest { /** - * Verifies that {@link LocationResolver#resolve(Location, IProject)} resolves the loacation in a file system - * context correctly. + * Verifies that {@link LocationResolver#resolveIfExists(Location, IProject)} resolves the loacation in a file + * system context correctly. */ @Test - public void resolveFileSystemLocation() { - final Location location = new Location("/tmp/pmd.xml", LocationContext.FILESYSTEM); - final IProject project = mock(IProject.class); + public void resolveIfExistsFileSystemLocation() throws IOException { + final Path ruleSetFile = Files.createTempFile(LocationResolverTest.class.getSimpleName(), ".xml"); + try { + final Location location = new Location(ruleSetFile.toString(), LocationContext.FILE_SYSTEM); + final IProject project = mock(IProject.class); + + final Optional result = LocationResolver.resolveIfExists(location, project); + + assertTrue("A valid file system location should resolve", result.isPresent()); + assertEquals("The resolved location in a file system context should be the provided location", + ruleSetFile.toString(), result.get()); + } finally { + Files.deleteIfExists(ruleSetFile); + } + } - final Optional result = LocationResolver.resolve(location, project); + /** + * Verifies that {@link LocationResolver#resolveIfExists(Location, IProject)} does not resolve the loacation in a + * file system context when the file is mossing. + */ + @Test + public void resolveIfExistsFileSystemLocationWithMissingFile() { + final Location location = new Location("/tmp/pmd.xml", LocationContext.FILE_SYSTEM); + final IProject project = mock(IProject.class); + + final Optional result = LocationResolver.resolveIfExists(location, project); - assertTrue("A valid file system location should resolve", result.isPresent()); - assertEquals("The resolved location in a file system context should be the provided location", - Paths.get("/tmp", "pmd.xml").toString(), result.get()); + assertFalse("The location should not resolve", result.isPresent()); } - + /** - * Verifies that {@link LocationResolver#resolve(Location, IProject)} does not throw an exception in a file system - * context if the path is invalid. + * Verifies that {@link LocationResolver#resolveIfExists(Location, IProject)} does not throw an exception in a file + * system context if the path is invalid. */ @Test - public void resolveFileSystemLocationWithInvalidPath() { - final Location location = new Location("\u0000:", LocationContext.FILESYSTEM); + public void resolveIfExistsFileSystemLocationWithInvalidPath() { + final Location location = new Location("\u0000:", LocationContext.FILE_SYSTEM); final IProject project = mock(IProject.class); - final Optional result = LocationResolver.resolve(location, project); + final Optional result = LocationResolver.resolveIfExists(location, project); assertFalse("The location should not resolve", result.isPresent()); } /** - * Verifies that {@link LocationResolver#resolve(Location, IProject)} resolves the loacation in a remote context - * correctly. + * Verifies that {@link LocationResolver#resolveIfExists(Location, IProject)} resolves the location in a remote + * context correctly. */ @Test - public void resolveRemoteLocation() { + public void resolveIfExistsRemoteLocation() throws IOException { + final Path ruleSetFile = Files.createTempFile(LocationResolverTest.class.getSimpleName(), ".xml"); + try { + final Location location = new Location(ruleSetFile.toUri().toString(), LocationContext.REMOTE); + final IProject project = mock(IProject.class); + + final Optional result = LocationResolver.resolveIfExists(location, project); + + assertTrue("A valid remote location should resolve", result.isPresent()); + assertEquals("The resolved location in a remote context should be the provided location", + ruleSetFile.toUri().toString(), result.get()); + } finally { + Files.deleteIfExists(ruleSetFile); + } + } + + /** + * Verifies that {@link LocationResolver#resolveIfExists(Location, IProject)} does not resolve the location in a + * remote context when the file does not exist. + */ + @Test + public void resolveIfExistsRemoteLocationWithMissingFile() { final Location location = new Location("http://example.org/pmd.xml", LocationContext.REMOTE); final IProject project = mock(IProject.class); - final Optional result = LocationResolver.resolve(location, project); + final Optional result = LocationResolver.resolveIfExists(location, project); - assertTrue("A valid remote location should resolve", result.isPresent()); - assertEquals("The resolved location in a remote context should be the provided location", "http://example.org/pmd.xml", - result.get()); + assertFalse("The location should not resolve", result.isPresent()); } - + /** - * Verifies that {@link LocationResolver#resolve(Location, IProject)} does not throw an exception in a remote - * context if the URI is invalid. + * Verifies that {@link LocationResolver#resolveIfExists(Location, IProject)} does not throw an exception in a + * remote context if the URI is invalid. */ @Test - public void resolveRemoteLocationWithInvalidURI() { + public void resolveIfExistsRemoteLocationWithInvalidURI() { final Location location = new Location("http:#", LocationContext.REMOTE); final IProject project = mock(IProject.class); - final Optional result = LocationResolver.resolve(location, project); + final Optional result = LocationResolver.resolveIfExists(location, project); assertFalse("The location should not resolve", result.isPresent()); } /** - * Verifies that {@link LocationResolver#resolve(Location, IProject)} resolves the location in a project context - * correctly. + * Verifies that {@link LocationResolver#resolveIfExists(Location, IProject)} resolves the location in a project + * context correctly. */ @Test - public void resolveProjectLocation() throws URISyntaxException { + public void resolveIfExistsProjectLocation() throws URISyntaxException, IOException { + final Path ruleSetFile = Files.createTempFile(LocationResolverTest.class.getSimpleName(), ".xml"); + try { + final Location location = new Location(ruleSetFile.getFileName().toString(), LocationContext.PROJECT); + final IProject project = mock(IProject.class); + when(project.getLocationURI()).thenReturn(ruleSetFile.getParent().toUri()); + + final Optional result = LocationResolver.resolveIfExists(location, project); + + assertTrue("A valid project location should resolve", result.isPresent()); + assertEquals("The resolved location in a project context should be the provided location appended to the project location", + ruleSetFile.toString(), result.get()); + } finally { + Files.deleteIfExists(ruleSetFile); + } + } + + /** + * Verifies that {@link LocationResolver#resolveIfExists(Location, IProject)} does not resolve the location in a + * project context when the rule set file does not exist. + */ + @Test + public void resolveIfExistsProjectLocationWithMissingFile() throws URISyntaxException { final Location location = new Location("pmd.xml", LocationContext.PROJECT); final IProject project = mock(IProject.class); when(project.getLocationURI()).thenReturn(new URI("file:///workspace/project/")); - final Optional result = LocationResolver.resolve(location, project); + final Optional result = LocationResolver.resolveIfExists(location, project); - assertTrue("A valid project location should resolve", result.isPresent()); - assertEquals("The resolved location in a project context should be the provided location appended to the project location", - Paths.get("/workspace", "project", "pmd.xml").toString(), result.get()); + assertFalse("The location should not resolve", result.isPresent()); } /** - * Verifies that {@link LocationResolver#resolve(Location, IProject)} does not throw an exception in a project - * context if the path is invalid. + * Verifies that {@link LocationResolver#resolveIfExists(Location, IProject)} does not throw an exception in a + * project context if the path is invalid. */ @Test - public void resolveProjectLocationWithInvalidPath() throws URISyntaxException { + public void resolveIfExistsProjectLocationWithInvalidPath() throws URISyntaxException { final Location location = new Location("\u0000:", LocationContext.PROJECT); final IProject project = mock(IProject.class); when(project.getLocationURI()).thenReturn(new URI("file:///workspace/project/")); - final Optional result = LocationResolver.resolve(location, project); + final Optional result = LocationResolver.resolveIfExists(location, project); assertFalse("The location should not resolve", result.isPresent()); } /** - * Verifies that {@link LocationResolver#resolve(Location, IProject)} resolves the location in a workspace context - * correctly. + * Verifies that {@link LocationResolver#resolveIfExists(Location, IProject)} resolves the location in a workspace + * context correctly. */ @Test - public void resolveWorkspaceLocation() throws URISyntaxException { + public void resolveIfExistsWorkspaceLocation() throws URISyntaxException, IOException { + final Path ruleSetFile = Files.createTempFile(LocationResolverTest.class.getSimpleName(), ".xml"); + try { + final Location location = new Location("project/" + ruleSetFile.getFileName().toString(), LocationContext.WORKSPACE); + final IProject project = mock(IProject.class); + final IWorkspace workspace = mock(IWorkspace.class); + final IWorkspaceRoot workspaceRoot = mock(IWorkspaceRoot.class); + when(project.getWorkspace()).thenReturn(workspace); + when(workspace.getRoot()).thenReturn(workspaceRoot); + when(workspaceRoot.getProject("project")).thenReturn(project); + when(project.getLocationURI()).thenReturn(ruleSetFile.getParent().toUri()); + + final Optional result = LocationResolver.resolveIfExists(location, project); + + assertTrue("A valid workspace location should resolve", result.isPresent()); + assertEquals("The resolved location in a workspace context should be the provided location appended to the workspace location", + ruleSetFile.toString(), result.get()); + } finally { + Files.deleteIfExists(ruleSetFile); + } + } + + /** + * Verifies that {@link LocationResolver#resolveIfExists(Location, IProject)} does not throw an exception in a + * workspace context if the rule set file does not exist. + */ + @Test + public void resolveIfExistsWorkspaceLocationWithMissingFile() throws URISyntaxException { final Location location = new Location("project/pmd.xml", LocationContext.WORKSPACE); final IProject project = mock(IProject.class); final IWorkspace workspace = mock(IWorkspace.class); @@ -143,21 +231,19 @@ public void resolveWorkspaceLocation() throws URISyntaxException { when(project.getWorkspace()).thenReturn(workspace); when(workspace.getRoot()).thenReturn(workspaceRoot); when(workspaceRoot.getProject("project")).thenReturn(project); - when(project.getLocationURI()).thenReturn(new URI("file:///workspace/project/")); + when(project.getLocationURI()).thenReturn(new URI("file:///workspace/project")); - final Optional result = LocationResolver.resolve(location, project); + final Optional result = LocationResolver.resolveIfExists(location, project); - assertTrue("A valid workspace location should resolve", result.isPresent()); - assertEquals("The resolved location in a workspace context should be the provided location appended to the workspace location", - Paths.get("/workspace", "project", "pmd.xml").toString(), result.get()); + assertFalse("The location should not resolve", result.isPresent()); } - + /** - * Verifies that {@link LocationResolver#resolve(Location, IProject)} does not throw an exception in a workspace - * context if the project does not exist. + * Verifies that {@link LocationResolver#resolveIfExists(Location, IProject)} does not throw an exception in a + * workspace context if the project does not exist. */ @Test - public void resolveWorkspaceLocationWithMissingProject() { + public void resolveIfExistsWorkspaceLocationWithMissingProject() { final Location location = new Location("MissingProject/pmd.xml", LocationContext.WORKSPACE); final IProject project = mock(IProject.class); final IWorkspace workspace = mock(IWorkspace.class); @@ -167,17 +253,17 @@ public void resolveWorkspaceLocationWithMissingProject() { when(workspaceRoot.getProject("MissingProject")).thenReturn(project); when(project.getLocationURI()).thenReturn(null); - final Optional result = LocationResolver.resolve(location, project); + final Optional result = LocationResolver.resolveIfExists(location, project); assertFalse("The location should not resolve", result.isPresent()); } /** - * Verifies that {@link LocationResolver#resolve(Location, IProject)} does not throw an exception in a workspace - * context if the path contains invalid characters. + * Verifies that {@link LocationResolver#resolveIfExists(Location, IProject)} does not throw an exception in a + * workspace context if the path contains invalid characters. */ @Test - public void resolveWorkspaceLocationWithInvalidPath() throws URISyntaxException { + public void resolveIfExistsWorkspaceLocationWithInvalidPath() throws URISyntaxException { final Location location = new Location("project/\u0000:", LocationContext.WORKSPACE); final IProject project = mock(IProject.class); final IWorkspace workspace = mock(IWorkspace.class); @@ -187,9 +273,73 @@ public void resolveWorkspaceLocationWithInvalidPath() throws URISyntaxException when(workspaceRoot.getProject("project")).thenReturn(project); when(project.getLocationURI()).thenReturn(new URI("file:///workspace/project/")); - final Optional result = LocationResolver.resolve(location, project); + final Optional result = LocationResolver.resolveIfExists(location, project); assertFalse("The location should not resolve", result.isPresent()); } + /** + * Verifies that {@link LocationResolver#resolve(Location, IProject)} resolves the location in a project context + * correctly. + */ + @Test + public void resolveWorkspaceLocation() throws URISyntaxException { + final Location location = new Location("project/path/pmd.xml", LocationContext.WORKSPACE); + final IProject project = mock(IProject.class); + final IWorkspace workspace = mock(IWorkspace.class); + final IWorkspaceRoot workspaceRoot = mock(IWorkspaceRoot.class); + when(project.getWorkspace()).thenReturn(workspace); + when(workspace.getRoot()).thenReturn(workspaceRoot); + when(workspaceRoot.getProject("project")).thenReturn(project); + when(project.getLocationURI()).thenReturn(new URI("file:///workspace/project/")); + + final String result = LocationResolver.resolve(location, project); + + assertEquals("The resolved location should be the project's path with the location's path appended", + Paths.get("/workspace", "project", "path", "pmd.xml"), Paths.get(result)); + } + + /** + * Verifies that {@link LocationResolver#resolve(Location, IProject)} resolves the location in a project context + * correctly. + */ + @Test + public void resolveProjectLocation() throws URISyntaxException { + final Location location = new Location("path/pmd.xml", LocationContext.PROJECT); + final IProject project = mock(IProject.class); + when(project.getLocationURI()).thenReturn(new URI("file:///workspace/project/")); + + final String result = LocationResolver.resolve(location, project); + + assertEquals("The resolved location should be the project's path with the location's path appended", + Paths.get("/workspace", "project", "path", "pmd.xml"), Paths.get(result)); + } + + /** + * Verifies that {@link LocationResolver#resolve(Location, IProject)} resolves the location in a file system context + * correctly. + */ + @Test + public void resolveFileSystemLocation() { + final Location location = new Location("/some/path/pmd.xml", LocationContext.FILE_SYSTEM); + final IProject project = mock(IProject.class); + + final String result = LocationResolver.resolve(location, project); + + assertEquals("The resolved location should just be the path", "/some/path/pmd.xml", result); + } + + /** + * Verifies that {@link LocationResolver#resolve(Location, IProject)} resolves the location in a remote context + * correctly. + */ + @Test + public void resolveRemoteLocation() { + final Location location = new Location("http://example.org/pmd.xml", LocationContext.FILE_SYSTEM); + final IProject project = mock(IProject.class); + + final String result = LocationResolver.resolve(location, project); + + assertEquals("The resolved location should be the URL", "http://example.org/pmd.xml", result); + } } diff --git a/ch.acanda.eclipse.pmd.core.tests/src/ch/acanda/eclipse/pmd/properties/PMDPropertyPageControllerTest.java b/ch.acanda.eclipse.pmd.core.tests/src/ch/acanda/eclipse/pmd/properties/PMDPropertyPageControllerTest.java index 172fa7f5..ab926e2c 100644 --- a/ch.acanda.eclipse.pmd.core.tests/src/ch/acanda/eclipse/pmd/properties/PMDPropertyPageControllerTest.java +++ b/ch.acanda.eclipse.pmd.core.tests/src/ch/acanda/eclipse/pmd/properties/PMDPropertyPageControllerTest.java @@ -12,7 +12,9 @@ package ch.acanda.eclipse.pmd.properties; import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import org.eclipse.core.resources.IProject; import org.junit.Test; import ch.acanda.eclipse.pmd.domain.RuleSetModel; @@ -37,7 +39,8 @@ public class PMDPropertyPageControllerTest { public void removeSelectedConfigurations() { final PMDPropertyPageController controller = new PMDPropertyPageController(); final PMDPropertyPageViewModel model = controller.getModel(); - model.setInitialState(true, ImmutableSortedSet.of()); + final IProject project = mock(IProject.class); + model.setInitialState(true, ImmutableSortedSet.of(), project); final ImmutableList ruleSets = createRuleSets(); model.setRuleSets(ruleSets); model.setActiveRuleSets(ruleSets.subList(0, 2)); @@ -58,7 +61,8 @@ public void removeSelectedConfigurations() { public void removeSelectedConfigurationsWithoutSelection() { final PMDPropertyPageController controller = new PMDPropertyPageController(); final PMDPropertyPageViewModel model = controller.getModel(); - model.setInitialState(true, ImmutableSortedSet.of()); + final IProject project = mock(IProject.class); + model.setInitialState(true, ImmutableSortedSet.of(), project); final ImmutableList ruleSets = createRuleSets(); model.setRuleSets(ruleSets); model.setActiveRuleSets(ruleSets.subList(0, 2)); @@ -84,10 +88,10 @@ public String apply(final RuleSetViewModel ruleSet) { } private ImmutableList createRuleSets() { - return ImmutableList.of(new RuleSetViewModel("A", "A-Type", "A-Location"), - new RuleSetViewModel("B", "B-Type", "B-Location"), - new RuleSetViewModel("C", "C-Type", "C-Location"), - new RuleSetViewModel("D", "D-Type", "D-Location")); + return ImmutableList.of(new RuleSetViewModel("A", "A-Type", "A-Location", true, "A-LocationToolTip"), + new RuleSetViewModel("B", "B-Type", "B-Location", false, "B-LocationToolTip"), + new RuleSetViewModel("C", "C-Type", "C-Location", true, "C-LocationToolTip"), + new RuleSetViewModel("D", "D-Type", "D-Location", false, "D-LocationToolTip")); } } diff --git a/ch.acanda.eclipse.pmd.core.tests/src/ch/acanda/eclipse/pmd/repository/ProjectModelSerializerTest.java b/ch.acanda.eclipse.pmd.core.tests/src/ch/acanda/eclipse/pmd/repository/ProjectModelSerializerTest.java index ca2d872a..a3f59c67 100644 --- a/ch.acanda.eclipse.pmd.core.tests/src/ch/acanda/eclipse/pmd/repository/ProjectModelSerializerTest.java +++ b/ch.acanda.eclipse.pmd.core.tests/src/ch/acanda/eclipse/pmd/repository/ProjectModelSerializerTest.java @@ -108,7 +108,7 @@ private String createXmlConfigurationWithoutRuleSets() { private Iterable createRuleSets() { return Arrays.asList(new RuleSetModel("Project Rule Set", new Location("pmd.xml", LocationContext.PROJECT)), new RuleSetModel("Workspace Rule Set", new Location("Projext X/pmd.xml", LocationContext.WORKSPACE)), - new RuleSetModel("Filesystem Rule Set", new Location("x:\\pmx.xml", LocationContext.FILESYSTEM)), + new RuleSetModel("Filesystem Rule Set", new Location("x:\\pmx.xml", LocationContext.FILE_SYSTEM)), new RuleSetModel("Remote Rule Set", new Location("http://example.org/pmd.xml", LocationContext.REMOTE))); } @@ -187,7 +187,7 @@ public void deserializeFilesystemRuleSetModel() throws IOException { final ProjectModel projectModel = new ProjectModelSerializer().deserialize(stream, "TestProjectName"); - assertRuleSetModel(projectModel, LocationContext.FILESYSTEM, "Filesystem Rule Set", "x:\\pmx.xml"); + assertRuleSetModel(projectModel, LocationContext.FILE_SYSTEM, "Filesystem Rule Set", "x:\\pmx.xml"); } /** diff --git a/ch.acanda.eclipse.pmd.core.tests/src/ch/acanda/eclipse/pmd/v07tov08/V07ToV08ConverterTest.java b/ch.acanda.eclipse.pmd.core.tests/src/ch/acanda/eclipse/pmd/v07tov08/V07ToV08ConverterTest.java index 4956ada5..02b8f5b1 100644 --- a/ch.acanda.eclipse.pmd.core.tests/src/ch/acanda/eclipse/pmd/v07tov08/V07ToV08ConverterTest.java +++ b/ch.acanda.eclipse.pmd.core.tests/src/ch/acanda/eclipse/pmd/v07tov08/V07ToV08ConverterTest.java @@ -60,7 +60,7 @@ public void convertWorkspaceLocationFallback() { final Location result = V07ToV08Converter.getLocation(config, workspaceRoot); assertEquals("Location path", Paths.get("/home", "somewhere", "else", "pmd.xml").toString(), result.getPath()); - assertEquals("Location context", LocationContext.FILESYSTEM, result.getContext()); + assertEquals("Location context", LocationContext.FILE_SYSTEM, result.getContext()); } @Test diff --git a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/builder/LocationResolver.java b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/builder/LocationResolver.java index 2f75a83f..fe4b59c2 100644 --- a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/builder/LocationResolver.java +++ b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/builder/LocationResolver.java @@ -12,8 +12,10 @@ package ch.acanda.eclipse.pmd.builder; import java.io.File; +import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; @@ -26,6 +28,8 @@ import com.google.common.base.Optional; /** + * Utility class to resolve a {@link Location}. + * * @author Philip Graf */ public final class LocationResolver { @@ -34,23 +38,28 @@ private LocationResolver() { // hide constructor of utility class } - public static Optional resolve(final Location location, final IProject project) { + /** + * Resolves the location and checks if it exist. + * + * @return The absolute location if it exist or {@code Optional#absent()} if it doesn't. + */ + public static Optional resolveIfExists(final Location location, final IProject project) { final Optional path; switch (location.getContext()) { case WORKSPACE: - path = resolveWorkspaceLocation(location, project); + path = resolveWorkspaceLocationIfExists(location, project); break; case PROJECT: - path = resolveProjectLocation(location, project); + path = resolveProjectLocationIfExists(location, project); break; - case FILESYSTEM: - path = resolveFileSystemLocation(location); + case FILE_SYSTEM: + path = resolveFileSystemLocationIfExists(location); break; case REMOTE: - path = resolveRemoteLocation(location); + path = resolveRemoteLocationIfExists(location); break; default: @@ -59,16 +68,41 @@ public static Optional resolve(final Location location, final IProject p return path; } - private static Optional resolveWorkspaceLocation(final Location location, final IProject project) { + /** + * Resolves a location. Unlike {@link #resolveIfExists(Location, IProject)} this also resolves if the location does + * not exist. + * + * @return The absolute location or {@code null} if it cannot be resolved. + */ + public static String resolve(final Location location, final IProject project) { + final String resolvedLocation; + switch (location.getContext()) { + case WORKSPACE: + final Path workspacePath = resolveWorkspaceLocation(location, project); + resolvedLocation = workspacePath == null ? null : workspacePath.toString(); + break; + + case PROJECT: + final Path path = Paths.get(project.getLocationURI()).resolve(toOSPath(location.getPath())); + resolvedLocation = path.normalize().toString(); + break; + + case FILE_SYSTEM: + case REMOTE: + resolvedLocation = location.getPath(); + break; + + default: + throw new IllegalStateException("Unknown location context: " + location.getContext()); + } + return resolvedLocation; + } + + private static Optional resolveWorkspaceLocationIfExists(final Location location, final IProject project) { try { - // format of the location's path: / - final Path locationPath = Paths.get(toOSPath(location.getPath())); - final String projectName = locationPath.getName(0).toString(); - final Path projectRelativePath = locationPath.subpath(1, locationPath.getNameCount()); - final IWorkspaceRoot root = project.getWorkspace().getRoot(); - final URI locationURI = root.getProject(projectName).getLocationURI(); - if (locationURI != null) { - return Optional.of(Paths.get(locationURI).resolve(projectRelativePath).toString()); + final Path path = resolveWorkspaceLocation(location, project); + if (path != null && Files.exists(path)) { + return Optional.of(path.toString()); } return Optional.absent(); } catch (final InvalidPathException e) { @@ -76,26 +110,43 @@ private static Optional resolveWorkspaceLocation(final Location location } } - private static Optional resolveProjectLocation(final Location location, final IProject project) { + private static Path resolveWorkspaceLocation(final Location location, final IProject project) { + // format of the location's path: / + final Path locationPath = Paths.get(toOSPath(location.getPath())); + final String projectName = locationPath.getName(0).toString(); + final IWorkspaceRoot root = project.getWorkspace().getRoot(); + final URI locationURI = root.getProject(projectName).getLocationURI(); + if (locationURI != null) { + final Path projectRelativePath = locationPath.subpath(1, locationPath.getNameCount()); + return Paths.get(locationURI).resolve(projectRelativePath); + } + return null; + } + + private static Optional resolveProjectLocationIfExists(final Location location, final IProject project) { try { - return Optional.of(Paths.get(project.getLocationURI()).resolve(toOSPath(location.getPath())).toString()); + final Path path = Paths.get(project.getLocationURI()).resolve(toOSPath(location.getPath())); + return Files.exists(path) ? Optional.of(path.toString()) : Optional.absent(); } catch (final InvalidPathException e) { return Optional.absent(); } } - private static Optional resolveFileSystemLocation(final Location location) { + private static Optional resolveFileSystemLocationIfExists(final Location location) { try { - return Optional.of(Paths.get(location.getPath()).toString()); + final Path path = Paths.get(location.getPath()); + return Files.exists(path) ? Optional.of(path.toString()) : Optional.absent(); } catch (final InvalidPathException e) { return Optional.absent(); } } - private static Optional resolveRemoteLocation(final Location location) { + private static Optional resolveRemoteLocationIfExists(final Location location) { try { - return Optional.of(new URI(location.getPath()).toString()); - } catch (final URISyntaxException e) { + final URI uri = new URI(location.getPath()); + uri.toURL().openStream().close(); + return Optional.of(uri.toString()); + } catch (final URISyntaxException | IOException e) { return Optional.absent(); } } diff --git a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/cache/RuleSetsCache.java b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/cache/RuleSetsCache.java index 91b30b23..5aa7b409 100644 --- a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/cache/RuleSetsCache.java +++ b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/cache/RuleSetsCache.java @@ -83,7 +83,7 @@ private void startWatchingRuleSetFiles(final ProjectModel projectModel) { for (final RuleSetModel ruleSetModel : projectModel.getRuleSets()) { if (ruleSetModel.getLocation().getContext() != LocationContext.REMOTE) { - final Optional resolvedLocation = LocationResolver.resolve(ruleSetModel.getLocation(), project); + final Optional resolvedLocation = LocationResolver.resolveIfExists(ruleSetModel.getLocation(), project); if (resolvedLocation.isPresent()) { final Path file = Paths.get(resolvedLocation.get()); try { diff --git a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/cache/RuleSetsCacheLoader.java b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/cache/RuleSetsCacheLoader.java index 4bfef07b..62765977 100644 --- a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/cache/RuleSetsCacheLoader.java +++ b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/cache/RuleSetsCacheLoader.java @@ -64,7 +64,7 @@ public ToReferenceId(final String projectName) { @Override public Optional apply(final RuleSetModel model) { - final Optional resolvedLocation = LocationResolver.resolve(model.getLocation(), project); + final Optional resolvedLocation = LocationResolver.resolveIfExists(model.getLocation(), project); return resolvedLocation.transform(new Function() { @Override public RuleSetReferenceId apply(final String location) { diff --git a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/domain/LocationContext.java b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/domain/LocationContext.java index 5b3e214e..4fa778c1 100644 --- a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/domain/LocationContext.java +++ b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/domain/LocationContext.java @@ -12,5 +12,5 @@ package ch.acanda.eclipse.pmd.domain; public enum LocationContext { - PROJECT, WORKSPACE, FILESYSTEM, REMOTE; + PROJECT, WORKSPACE, FILE_SYSTEM, REMOTE; } \ No newline at end of file diff --git a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/LocationLabelProvider.java b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/LocationLabelProvider.java new file mode 100644 index 00000000..7d5f1315 --- /dev/null +++ b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/LocationLabelProvider.java @@ -0,0 +1,48 @@ +// ===================================================================== +// +// Copyright (C) 2012 - 2015, Philip Graf +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// which accompanies this distribution, and is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// ===================================================================== + +package ch.acanda.eclipse.pmd.properties; + +import org.eclipse.swt.graphics.Image; + +import ch.acanda.eclipse.pmd.properties.PMDPropertyPageViewModel.RuleSetViewModel; + +/** + * Provides the label and tool tip for the location column. + * + * @author Philip Graf + */ +final class LocationLabelProvider extends RuleSetConfigurationLabelProvider { + + protected LocationLabelProvider(final PMDPropertyPageViewModel model) { + super(model); + } + + @Override + protected String getText(final RuleSetViewModel ruleSet) { + return ruleSet.getLocation(); + } + + @Override + public String getToolTipText(final Object element) { + final RuleSetViewModel ruleSet = toRuleSet(element); + if (ruleSet.isLocationValid()) { + return ruleSet.getLocationToolTip(); + } + return getErrorMessage(ruleSet); + } + + @Override + public Image getToolTipImage(final Object element) { + return getImage(toRuleSet(element)); + } + +} \ No newline at end of file diff --git a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/NameLabelProvider.java b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/NameLabelProvider.java new file mode 100644 index 00000000..8424ffd2 --- /dev/null +++ b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/NameLabelProvider.java @@ -0,0 +1,53 @@ +// ===================================================================== +// +// Copyright (C) 2012 - 2015, Philip Graf +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// which accompanies this distribution, and is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// ===================================================================== + +package ch.acanda.eclipse.pmd.properties; + +import org.eclipse.swt.graphics.Image; + +import ch.acanda.eclipse.pmd.properties.PMDPropertyPageViewModel.RuleSetViewModel; + +/** + * Provides the label, image and tool tip for the name column. + * + * @author Philip Graf + */ +final class NameLabelProvider extends RuleSetConfigurationLabelProvider { + + protected NameLabelProvider(final PMDPropertyPageViewModel model) { + super(model); + } + + @Override + protected String getText(final RuleSetViewModel ruleSet) { + return ruleSet.getName(); + } + + @Override + public Image getImage(final Object element) { + return getImage(toRuleSet(element)); + } + + @Override + public String getToolTipText(final Object element) { + final RuleSetViewModel ruleSet = toRuleSet(element); + if (!ruleSet.isLocationValid()) { + return getErrorMessage(ruleSet); + } + return null; + } + + @Override + public Image getToolTipImage(final Object element) { + return getImage(element); + } + +} \ No newline at end of file diff --git a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/PMDPropertyPageController.java b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/PMDPropertyPageController.java index 7ae579cc..e0b43474 100644 --- a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/PMDPropertyPageController.java +++ b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/PMDPropertyPageController.java @@ -65,17 +65,17 @@ public void init(final IProject project) { final WorkspaceModel workspaceModel = PMDPlugin.getDefault().getWorkspaceModel(); projectModel = workspaceModel.getOrCreateProject(project.getName()); - model.setInitialState(projectModel.isPMDEnabled(), projectModel.getRuleSets()); + model.setInitialState(projectModel.isPMDEnabled(), projectModel.getRuleSets(), project); final ImmutableSortedSet.Builder ruleSetBuilder = ImmutableSortedSet.orderedBy(ProjectModel.RULE_SET_COMPARATOR); for (final ProjectModel projectModel : workspaceModel.getProjects()) { ruleSetBuilder.addAll(projectModel.getRuleSets()); } - model.setRuleSets(ImmutableList.copyOf(toViewModels(ruleSetBuilder.build()))); + model.setRuleSets(ImmutableList.copyOf(toViewModels(ruleSetBuilder.build(), project))); reset(); } public void reset() { - model.setActiveRuleSets(ImmutableSet.copyOf(toViewModels(projectModel.getRuleSets()))); + model.setActiveRuleSets(ImmutableSet.copyOf(toViewModels(projectModel.getRuleSets(), project))); model.setSelectedRuleSets(ImmutableList.of()); model.setPMDEnabled(projectModel.isPMDEnabled()); } @@ -108,7 +108,7 @@ public void addRuleSetConfiguration(final Shell shell) { dialog.setPageSize(300, SWT.DEFAULT); final int result = dialog.open(); if (result == Window.OK && wizard.getRuleSetModel() != null) { - final RuleSetViewModel viewModel = toViewModel(wizard.getRuleSetModel()); + final RuleSetViewModel viewModel = toViewModel(wizard.getRuleSetModel(), project); model.addRuleSet(viewModel); final HashSet activeConfigs = new HashSet<>(model.getActiveRuleSets()); activeConfigs.add(viewModel); diff --git a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/PMDPropertyPageModelTransformer.java b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/PMDPropertyPageModelTransformer.java index 3c4db44f..758673d1 100644 --- a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/PMDPropertyPageModelTransformer.java +++ b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/PMDPropertyPageModelTransformer.java @@ -11,11 +11,15 @@ package ch.acanda.eclipse.pmd.properties; -import static ch.acanda.eclipse.pmd.domain.LocationContext.FILESYSTEM; +import static ch.acanda.eclipse.pmd.domain.LocationContext.FILE_SYSTEM; import static ch.acanda.eclipse.pmd.domain.LocationContext.PROJECT; import static ch.acanda.eclipse.pmd.domain.LocationContext.REMOTE; import static ch.acanda.eclipse.pmd.domain.LocationContext.WORKSPACE; import static com.google.common.collect.Iterables.transform; + +import org.eclipse.core.resources.IProject; + +import ch.acanda.eclipse.pmd.builder.LocationResolver; import ch.acanda.eclipse.pmd.domain.Location; import ch.acanda.eclipse.pmd.domain.LocationContext; import ch.acanda.eclipse.pmd.domain.RuleSetModel; @@ -26,30 +30,20 @@ /** * Transforms the domain model to and from the PMD property page's view model. - * + * * @author Philip Graf */ public final class PMDPropertyPageModelTransformer { - + /** * Maps a location context to a label of a rule set type and vice versa. The label is used as the value shown in the * "Type" column of the table in the PMD property page. */ private static final ImmutableBiMap CONTEXT_TYPE_MAP = ImmutableBiMap.of(WORKSPACE, "Workspace", PROJECT, "Project", - FILESYSTEM, "File System", + FILE_SYSTEM, "File System", REMOTE, "Remote"); - - /** - * Transforms a rule set domain model to a rule set view model. - */ - private static final Function TO_RULESETVIEWMODEL = new Function() { - @Override - public RuleSetViewModel apply(final RuleSetModel domainModel) { - return toViewModel(domainModel); - } - }; - + /** * Transforms a rule set view model to a rule set domain model. */ @@ -59,29 +53,36 @@ public RuleSetModel apply(final RuleSetViewModel viewModel) { return toDomainModel(viewModel); } }; - + private PMDPropertyPageModelTransformer() { // hide constructor of utility class } - + /** * Returns an iterable transforming a rule set domain model to a rule set view model on demand, i.e. the returned * iterable is a view on the supplied iterable. */ - public static Iterable toViewModels(final Iterable domainModels) { - return transform(domainModels, TO_RULESETVIEWMODEL); + public static Iterable toViewModels(final Iterable domainModels, final IProject project) { + return transform(domainModels, new Function() { + @Override + public RuleSetViewModel apply(final RuleSetModel domainModel) { + return toViewModel(domainModel, project); + } + }); } - + /** * Transforms a rule set domain model to a rule set view model. */ - public static RuleSetViewModel toViewModel(final RuleSetModel ruleSetModel) { + public static RuleSetViewModel toViewModel(final RuleSetModel ruleSetModel, final IProject project) { final String name = ruleSetModel.getName(); final String type = CONTEXT_TYPE_MAP.get(ruleSetModel.getLocation().getContext()); final String location = ruleSetModel.getLocation().getPath(); - return new RuleSetViewModel(name, type, location); + final boolean isValidLocation = LocationResolver.resolveIfExists(ruleSetModel.getLocation(), project).isPresent(); + final String resolvedLocation = LocationResolver.resolve(ruleSetModel.getLocation(), project); + return new RuleSetViewModel(name, type, location, isValidLocation, resolvedLocation); } - + /** * Returns an iterable transforming a rule set view model to a rule set domain model on demand, i.e. the returned * iterable is a view on the supplied iterable. @@ -89,7 +90,7 @@ public static RuleSetViewModel toViewModel(final RuleSetModel ruleSetModel) { public static Iterable toDomainModels(final Iterable viewModels) { return transform(viewModels, TO_RULESETMODEL); } - + /** * Transforms a rule set view model to a rule set domain model. */ diff --git a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/PMDPropertyPageViewModel.java b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/PMDPropertyPageViewModel.java index ca4a6667..b11153d3 100644 --- a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/PMDPropertyPageViewModel.java +++ b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/PMDPropertyPageViewModel.java @@ -17,6 +17,8 @@ import java.util.List; import java.util.Set; +import org.eclipse.core.resources.IProject; + import ch.acanda.eclipse.pmd.domain.RuleSetModel; import ch.acanda.eclipse.pmd.ui.model.ValidationResult; import ch.acanda.eclipse.pmd.ui.model.ViewModel; @@ -33,6 +35,9 @@ */ final class PMDPropertyPageViewModel extends ViewModel { + public static final String RULE_SETS = "ruleSets"; + public static final String ACTIVE_RULE_SETS = "activeRuleSets"; + /** * All available rule sets of the entire workspace. */ @@ -65,16 +70,16 @@ public ImmutableList getRuleSets() { } public void setRuleSets(final ImmutableList ruleSets) { - setProperty("ruleSets", this.ruleSets, this.ruleSets = ruleSets); + setProperty(RULE_SETS, this.ruleSets, this.ruleSets = ruleSets); } /** * Sets the initial state, i.e. the state of the view model before any changes were made. This state is used by * {@link #updateDirty()} so it must be set before any properties of the view model are changed. */ - public void setInitialState(final boolean isPmdEnabled, final ImmutableSortedSet ruleSets) { + public void setInitialState(final boolean isPmdEnabled, final ImmutableSortedSet ruleSets, final IProject project) { initialIsPMDEnabled = isPMDEnabled; - initialActiveRuleSets = toViewModels(ruleSets); + initialActiveRuleSets = toViewModels(ruleSets, project); } @Override @@ -110,11 +115,11 @@ public List getSelectedRuleSets() { } public void setActiveRuleSets(final Iterable ruleSets) { - setProperty("activeRuleSets", activeRuleSets, activeRuleSets = ImmutableSet.copyOf(ruleSets)); + setProperty(ACTIVE_RULE_SETS, activeRuleSets, activeRuleSets = ImmutableSet.copyOf(ruleSets)); } public void setActiveRuleSets(final Set ruleSets) { - setProperty("activeRuleSets", activeRuleSets, activeRuleSets = ImmutableSet.copyOf(ruleSets)); + setProperty(ACTIVE_RULE_SETS, activeRuleSets, activeRuleSets = ImmutableSet.copyOf(ruleSets)); } public Set getActiveRuleSets() { @@ -126,11 +131,16 @@ static final class RuleSetViewModel { private final String name; private final String type; private final String location; + private final boolean isLocationValid; + private final String locationToolTip; - public RuleSetViewModel(final String name, final String type, final String location) { + public RuleSetViewModel(final String name, final String type, final String location, final boolean isLocationValid, + final String locationToolTip) { this.name = name; this.type = type; this.location = location; + this.isLocationValid = isLocationValid; + this.locationToolTip = locationToolTip; } public String getName() { @@ -145,6 +155,14 @@ public String getLocation() { return location; } + public boolean isLocationValid() { + return isLocationValid; + } + + public String getLocationToolTip() { + return locationToolTip; + } + @Override public int hashCode() { final int prime = 31; diff --git a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/RuleSetConfigurationLabelProvider.java b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/RuleSetConfigurationLabelProvider.java new file mode 100644 index 00000000..af86b484 --- /dev/null +++ b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/RuleSetConfigurationLabelProvider.java @@ -0,0 +1,64 @@ +// ===================================================================== +// +// Copyright (C) 2012 - 2015, Philip Graf +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// which accompanies this distribution, and is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// ===================================================================== + +package ch.acanda.eclipse.pmd.properties; + +import java.text.MessageFormat; + +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.swt.graphics.Image; +import org.eclipse.ui.ISharedImages; +import org.eclipse.ui.PlatformUI; + +import ch.acanda.eclipse.pmd.properties.PMDPropertyPageViewModel.RuleSetViewModel; + +/** + * Base class for the table's column label providers. + * + * @author Philip Graf + */ +abstract class RuleSetConfigurationLabelProvider extends ColumnLabelProvider { + + private final PMDPropertyPageViewModel model; + + protected RuleSetConfigurationLabelProvider(final PMDPropertyPageViewModel model) { + this.model = model; + } + + @Override + public String getText(final Object element) { + return getText(toRuleSet(element)); + } + + protected abstract String getText(RuleSetViewModel ruleSet); + + protected RuleSetViewModel toRuleSet(final Object element) { + return (RuleSetViewModel) element; + } + + protected Image getImage(final RuleSetViewModel ruleSet) { + if (!ruleSet.isLocationValid()) { + final String key = isActive(ruleSet) ? ISharedImages.IMG_OBJS_ERROR_TSK : ISharedImages.IMG_OBJS_WARN_TSK; + return PlatformUI.getWorkbench().getSharedImages().getImage(key); + } + return null; + } + + protected boolean isActive(final RuleSetViewModel ruleSet) { + return model.getActiveRuleSets().contains(ruleSet); + } + + protected String getErrorMessage(final RuleSetViewModel ruleSet) { + final String template = "The file {0} does not exist."; + return MessageFormat.format(template, ruleSet.getLocationToolTip()); + } + +} \ No newline at end of file diff --git a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/RuleSetConfigurationTable.java b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/RuleSetConfigurationTable.java index 42082d55..e071cdfd 100644 --- a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/RuleSetConfigurationTable.java +++ b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/RuleSetConfigurationTable.java @@ -11,23 +11,25 @@ package ch.acanda.eclipse.pmd.properties; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + import org.eclipse.core.databinding.DataBindingContext; import org.eclipse.core.databinding.beans.BeansObservables; -import org.eclipse.core.databinding.beans.PojoObservables; import org.eclipse.core.databinding.observable.Realm; import org.eclipse.core.databinding.observable.list.IObservableList; -import org.eclipse.core.databinding.observable.map.IObservableMap; import org.eclipse.core.databinding.observable.set.IObservableSet; import org.eclipse.core.databinding.observable.value.IObservableValue; import org.eclipse.jface.databinding.swt.SWTObservables; import org.eclipse.jface.databinding.viewers.ObservableListContentProvider; -import org.eclipse.jface.databinding.viewers.ObservableMapLabelProvider; import org.eclipse.jface.databinding.viewers.ViewersObservables; import org.eclipse.jface.layout.TableColumnLayout; import org.eclipse.jface.viewers.CheckboxTableViewer; import org.eclipse.jface.viewers.ColumnPixelData; +import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; import org.eclipse.jface.viewers.ColumnWeightData; import org.eclipse.jface.viewers.TableViewerColumn; +import org.eclipse.jface.window.ToolTip; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; @@ -39,7 +41,7 @@ /** * A composite containing a single checkbox table viewer showing the rule set configurations. - * + * * @author Philip Graf */ final class RuleSetConfigurationTable extends Composite { @@ -60,34 +62,35 @@ public RuleSetConfigurationTable(final Composite parent, final PMDPropertyPageVi table = tableViewer.getTable(); table.setHeaderVisible(true); table.setLinesVisible(true); + ColumnViewerToolTipSupport.enableFor(tableViewer, ToolTip.NO_RECREATE); SWTBotID.set(table, SWTBotID.RULESETS); final TableViewerColumn nameViewerColumn = new TableViewerColumn(tableViewer, SWT.NONE); final TableColumn nameColumn = nameViewerColumn.getColumn(); tableColumnLayout.setColumnData(nameColumn, new ColumnWeightData(1, 50, true)); nameColumn.setText("Name"); + nameViewerColumn.setLabelProvider(new NameLabelProvider(model)); final TableViewerColumn typeViewerColumn = new TableViewerColumn(tableViewer, SWT.NONE); final TableColumn typeColumn = typeViewerColumn.getColumn(); tableColumnLayout.setColumnData(typeColumn, new ColumnPixelData(75, true, true)); typeColumn.setText("Type"); + typeViewerColumn.setLabelProvider(new TypeLabelProvider(model)); final TableViewerColumn locationViewerColumn = new TableViewerColumn(tableViewer, SWT.NONE); final TableColumn locationColumn = locationViewerColumn.getColumn(); tableColumnLayout.setColumnData(locationColumn, new ColumnWeightData(2, 50, true)); locationColumn.setText("Location"); + locationViewerColumn.setLabelProvider(new LocationLabelProvider(model)); initDataBindings(); + initListeners(); } private void initDataBindings() { final DataBindingContext bindingContext = new DataBindingContext(); // final ObservableListContentProvider listContentProvider = new ObservableListContentProvider(); - final IObservableMap[] observeMaps = PojoObservables.observeMaps(listContentProvider.getKnownElements(), - RuleSetViewModel.class, - new String[] { "name", "type", "location" }); - tableViewer.setLabelProvider(new ObservableMapLabelProvider(observeMaps)); tableViewer.setContentProvider(listContentProvider); // final IObservableList ruleSetsObserveList = BeansObservables.observeList(Realm.getDefault(), model, "ruleSets"); @@ -107,4 +110,14 @@ private void initDataBindings() { bindingContext.bindSet(tableViewerObserveCheckedElements, activeConfigurationsObserveSet, null, null); } + private void initListeners() { + model.addPropertyChangeListener(PMDPropertyPageViewModel.ACTIVE_RULE_SETS, new PropertyChangeListener() { + @Override + public void propertyChange(final PropertyChangeEvent evt) { + // this updates the column image of invalid configurations when their checked state changes + tableViewer.refresh(true); + } + }); + } + } diff --git a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/TypeLabelProvider.java b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/TypeLabelProvider.java new file mode 100644 index 00000000..3e39352f --- /dev/null +++ b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/properties/TypeLabelProvider.java @@ -0,0 +1,32 @@ +// ===================================================================== +// +// Copyright (C) 2012 - 2015, Philip Graf +// +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// which accompanies this distribution, and is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// ===================================================================== + +package ch.acanda.eclipse.pmd.properties; + +import ch.acanda.eclipse.pmd.properties.PMDPropertyPageViewModel.RuleSetViewModel; + +/** + * Provides the label for the type column. + * + * @author Philip Graf + */ +final class TypeLabelProvider extends RuleSetConfigurationLabelProvider { + + protected TypeLabelProvider(final PMDPropertyPageViewModel model) { + super(model); + } + + @Override + protected String getText(final RuleSetViewModel ruleSet) { + return ruleSet.getType(); + } + +} \ No newline at end of file diff --git a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/repository/ProjectConfigurationContentHandler.java b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/repository/ProjectConfigurationContentHandler.java index d2ba32c6..7090f92f 100644 --- a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/repository/ProjectConfigurationContentHandler.java +++ b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/repository/ProjectConfigurationContentHandler.java @@ -87,7 +87,7 @@ private LocationContext getContext(final String refcontext) { break; case ATTRIBUTE_VALUE_FILESYSTEM: - context = LocationContext.FILESYSTEM; + context = LocationContext.FILE_SYSTEM; break; case ATTRIBUTE_VALUE_REMOTE: diff --git a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/repository/RuleSetConfigurationToXMLTag.java b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/repository/RuleSetConfigurationToXMLTag.java index 3a47f9ee..dd3e3ab5 100644 --- a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/repository/RuleSetConfigurationToXMLTag.java +++ b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/repository/RuleSetConfigurationToXMLTag.java @@ -57,7 +57,7 @@ private String getContext(final RuleSetModel ruleSet) { case PROJECT: value = ATTRIBUTE_VALUE_PROJECT; break; - case FILESYSTEM: + case FILE_SYSTEM: value = ATTRIBUTE_VALUE_FILESYSTEM; break; case REMOTE: diff --git a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/v07tov08/V07ToV08Converter.java b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/v07tov08/V07ToV08Converter.java index 50634026..46db1a8e 100644 --- a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/v07tov08/V07ToV08Converter.java +++ b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/v07tov08/V07ToV08Converter.java @@ -105,7 +105,7 @@ private static Location convertWorkspacePath(final RuleSetConfiguration config, } // fallback: if the workspace path cannot be converted, // replace the workspace location with a file system location - return new Location(target.toString(), LocationContext.FILESYSTEM); + return new Location(target.toString(), LocationContext.FILE_SYSTEM); } private static LocationContext getContext(final RuleSetConfiguration config) { @@ -115,7 +115,7 @@ private static LocationContext getContext(final RuleSetConfiguration config) { } else if (config instanceof ProjectRuleSetConfiguration) { context = LocationContext.PROJECT; } else if (config instanceof FileSystemRuleSetConfiguration) { - context = LocationContext.FILESYSTEM; + context = LocationContext.FILE_SYSTEM; } else if (config instanceof RemoteRuleSetConfiguration) { context = LocationContext.REMOTE; } else { diff --git a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/wizard/AddRuleSetConfigurationController.java b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/wizard/AddRuleSetConfigurationController.java index 13d3705c..51cda81e 100644 --- a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/wizard/AddRuleSetConfigurationController.java +++ b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/wizard/AddRuleSetConfigurationController.java @@ -130,7 +130,7 @@ private LocationContext getLocationContext() { locationContext = LocationContext.PROJECT; } else if (model.isFileSystemTypeSelected()) { - locationContext = LocationContext.FILESYSTEM; + locationContext = LocationContext.FILE_SYSTEM; } else if (model.isRemoteTypeSelected()) { locationContext = LocationContext.REMOTE; diff --git a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/wizard/AddRuleSetConfigurationModel.java b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/wizard/AddRuleSetConfigurationModel.java index 4ca65562..55e61835 100644 --- a/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/wizard/AddRuleSetConfigurationModel.java +++ b/ch.acanda.eclipse.pmd.core/src/ch/acanda/eclipse/pmd/wizard/AddRuleSetConfigurationModel.java @@ -255,11 +255,11 @@ private Optional getAbsoluteLocation() { } else if (isProjectTypeSelected) { locationContext = LocationContext.PROJECT; } else if (isFileSystemTypeSelected) { - locationContext = LocationContext.FILESYSTEM; + locationContext = LocationContext.FILE_SYSTEM; } else { throw new IllegalStateException("Unknown location type"); } - final Optional resolvedLocation = LocationResolver.resolve(new Location(location, locationContext), project); + final Optional resolvedLocation = LocationResolver.resolveIfExists(new Location(location, locationContext), project); if (resolvedLocation.isPresent()) { return Optional.of(Paths.get(resolvedLocation.get())); }