// ===================================================================== // // Copyright (C) 2012 - 2016, 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.wizard; import static ch.acanda.eclipse.pmd.ui.model.ValidationUtil.errorIfBlank; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.text.MessageFormat; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetFactory; import net.sourceforge.pmd.RuleSetNotFoundException; 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.ui.model.ValidationProblem; import ch.acanda.eclipse.pmd.ui.model.ValidationProblem.Severity; import ch.acanda.eclipse.pmd.ui.model.ValidationResult; import ch.acanda.eclipse.pmd.ui.model.ViewModel; import com.google.common.base.Optional; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList.Builder; import com.google.common.collect.ImmutableSet; /** * View model for the wizard page to add a new file system rule set configuration. * * @author Philip Graf */ class AddRuleSetConfigurationModel extends ViewModel { public static final String FILE_SYSTEM_TYPE_SELECTED = "fileSystemTypeSelected"; public static final String WORKSPACE_TYPE_SELECTED = "workspaceTypeSelected"; public static final String PROJECT_TYPE_SELECTED = "projectTypeSelected"; public static final String REMOTE_TYPE_SELECTED = "remoteTypeSelected"; public static final String BROWSE_ENABLED = "browseEnabled"; public static final String LOCATION = "location"; public static final String NAME = "name"; public static final String RULES = "rules"; private final IProject project; private String name; private String location; private boolean isFileSystemTypeSelected; private boolean isWorkspaceTypeSelected; private boolean isProjectTypeSelected; private boolean isRemoteTypeSelected; private boolean isBrowseEnabled; /** * This property is derived from {@link #location}. If {@link #location} is valid this list contains the rules of * the selected rule set, otherwise it is empty. It is never {@code null}. */ private ImmutableList<Rule> rules = ImmutableList.of(); public AddRuleSetConfigurationModel(final IProject project) { this.project = project; } @Override protected boolean updateDirty() { return !(Strings.isNullOrEmpty(name) && Strings.isNullOrEmpty(location)); } protected void reset() { setName(null); setLocation(null); setWorkspaceTypeSelected(true); setProjectTypeSelected(false); setFileSystemTypeSelected(false); } public String getName() { return name; } public void setName(final String name) { setProperty(NAME, this.name, this.name = name); } public String getLocation() { return location; } public void setLocation(final String location) { setProperty(LOCATION, this.location, this.location = location); } public ImmutableList<Rule> getRules() { return rules; } private void setRules(final ImmutableList<Rule> rules) { assert rules != null; setProperty(RULES, this.rules, this.rules = rules); } public boolean isFileSystemTypeSelected() { return isFileSystemTypeSelected; } public void setFileSystemTypeSelected(final boolean isFileSystemTypeSelected) { setProperty(FILE_SYSTEM_TYPE_SELECTED, this.isFileSystemTypeSelected, this.isFileSystemTypeSelected = isFileSystemTypeSelected); updateBrowseEnabled(); } public boolean isWorkspaceTypeSelected() { return isWorkspaceTypeSelected; } public void setWorkspaceTypeSelected(final boolean isWorkspaceTypeSelected) { setProperty(WORKSPACE_TYPE_SELECTED, this.isWorkspaceTypeSelected, this.isWorkspaceTypeSelected = isWorkspaceTypeSelected); updateBrowseEnabled(); } public boolean isProjectTypeSelected() { return isProjectTypeSelected; } public void setProjectTypeSelected(final boolean isProjectTypeSelected) { setProperty(PROJECT_TYPE_SELECTED, this.isProjectTypeSelected, this.isProjectTypeSelected = isProjectTypeSelected); updateBrowseEnabled(); } public boolean isRemoteTypeSelected() { return isRemoteTypeSelected; } public void setRemoteTypeSelected(final boolean isRemoteTypeSelected) { setProperty(REMOTE_TYPE_SELECTED, this.isRemoteTypeSelected, this.isRemoteTypeSelected = isRemoteTypeSelected); updateBrowseEnabled(); } private void updateBrowseEnabled() { setBrowseEnabled(!isRemoteTypeSelected); } public boolean isBrowseEnabled() { return isBrowseEnabled; } public void setBrowseEnabled(final boolean isBrowseEnabled) { setProperty(BROWSE_ENABLED, this.isBrowseEnabled, this.isBrowseEnabled = isBrowseEnabled); } @Override protected ImmutableSet<String> createValidatedPropertiesSet() { return ImmutableSet.of(LOCATION, NAME); } @Override protected void validate(final String propertyName, final ValidationResult validationResult) { validateName(validationResult); validateLocation(propertyName, validationResult); } /** * Validates the location of the rule set configuration and sets or resets the property {@link #rules} depending on * whether {@link #location} contains a valid rule set configuration location or not. */ @SuppressWarnings("PMD.AvoidCatchingGenericException") private void validateLocation(final String propertyName, final ValidationResult result) { final Builder<Rule> rules = ImmutableList.builder(); String ruleSetName = null; if (!errorIfBlank(LOCATION, location, "Please enter the location of the rule set configuration", result)) { RuleSet ruleSet = null; try { final String referenceId; if (isRemoteTypeSelected) { referenceId = validateRemoteLocation(result); } else { referenceId = validateLocalLocation(result); } if (referenceId != null) { ruleSet = new RuleSetFactory().createRuleSet(referenceId); ruleSetName = ruleSet.getName(); rules.addAll(ruleSet.getRules()); } } catch (final RuleSetNotFoundException | RuntimeException e) { // the rule set location is invalid - the validation problem will be added below } if (ruleSet == null || ruleSet.getRules().isEmpty()) { result.add(new ValidationProblem(LOCATION, Severity.ERROR, "The rule set configuration at the given location is invalid")); } } if (LOCATION.equals(propertyName)) { setRules(rules.build()); setName(ruleSetName == null ? "" : ruleSetName); } } private String validateRemoteLocation(final ValidationResult result) { String referenceId = null; try { final URI uri = new URI(location); try (InputStream stream = uri.toURL().openStream()) { final Path tempFile = Files.createTempFile("eclipse-pmd-remote-", ".xml"); Files.copy(stream, tempFile, StandardCopyOption.REPLACE_EXISTING); tempFile.toFile().deleteOnExit(); referenceId = tempFile.toString(); } } catch (final URISyntaxException e) { result.add(new ValidationProblem(LOCATION, Severity.ERROR, "The location is not a valid URI")); } catch (final IOException e) { result.add(new ValidationProblem(LOCATION, Severity.ERROR, "The resource at the given URI does not exist")); } return referenceId; } private String validateLocalLocation(final ValidationResult result) { String referenceId = null; final Optional<Path> absoluteLocation = getAbsoluteLocation(); if (absoluteLocation.isPresent()) { if (Files.exists(absoluteLocation.get())) { referenceId = absoluteLocation.get().toString(); } else { final String msg = "The location {0} which resolves to {1} does not point to an existing file"; result.add(new ValidationProblem(LOCATION, Severity.ERROR, MessageFormat.format(msg, location, absoluteLocation))); } } else { final String msg = "The location {0} cannot be resolved"; result.add(new ValidationProblem(LOCATION, Severity.ERROR, MessageFormat.format(msg, location))); } return referenceId; } private Optional<Path> getAbsoluteLocation() { final LocationContext locationContext; if (isWorkspaceTypeSelected) { locationContext = LocationContext.WORKSPACE; } else if (isProjectTypeSelected) { locationContext = LocationContext.PROJECT; } else if (isFileSystemTypeSelected) { locationContext = LocationContext.FILE_SYSTEM; } else { throw new IllegalStateException("Unknown location type"); } final Optional<String> resolvedLocation = LocationResolver.resolveIfExists(new Location(location, locationContext), project); if (resolvedLocation.isPresent()) { return Optional.of(Paths.get(resolvedLocation.get())); } return Optional.absent(); } private void validateName(final ValidationResult result) { errorIfBlank(NAME, name, "Please enter a name for this rule set configuration", result); } }