Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CAY-2846 Modeler: Allow to disable some validation rules in the project #617

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,36 +25,51 @@
import org.apache.cayenne.util.Util;
import org.apache.cayenne.validation.ValidationResult;

import java.util.function.Supplier;

/**
* Base validation for all query types
*/
class BaseQueryValidator extends ConfigurationNodeValidator {
abstract class BaseQueryValidator<T extends QueryDescriptor> extends ConfigurationNodeValidator<T> {

void validateCacheGroup(QueryDescriptor query, ValidationResult validationResult) {
String cacheGroup = query.getProperty(QueryMetadata.CACHE_GROUPS_PROPERTY);
if(cacheGroup != null && cacheGroup.contains(",")) {
addFailure(validationResult, query, "Invalid cache group \"%s\", " +
"multiple groups are deprecated", cacheGroup);
}
/**
* @param configSupplier the config defining the behavior of this validator.
* @since 5.0
*/
public BaseQueryValidator(Supplier<ValidationConfig> configSupplier) {
super(configSupplier);
}

@Override
public void validate(T node, ValidationResult validationResult) {
validateQuery(node, validationResult);
}

protected Performer<T> validateQuery(T query, ValidationResult validationResult) {
return on(query, validationResult)
.performIfEnabled(Inspection.QUERY_NO_NAME, this::checkForName)
.performIfEnabled(Inspection.QUERY_NAME_DUPLICATE, this::checkForNameDuplicates)
.performIfEnabled(Inspection.QUERY_MULTI_CACHE_GROUP, this::checkForMultiCacheGroup);
}

void validateName(QueryDescriptor query, ValidationResult validationResult) {
final String name = query.getName();
void checkForName(T query, ValidationResult validationResult) {

// Must have name
String name = query.getName();
if (Util.isEmptyString(name)) {
addFailure(validationResult, query, "Unnamed " + query.getType());
return;
}
}

void checkForNameDuplicates(T query, ValidationResult validationResult) {
String name = query.getName();
DataMap map = query.getDataMap();
if (map == null) {
if (map == null || Util.isEmptyString(name)) {
return;
}

// check for duplicate names in the parent context
if(hasDuplicateQueryDescriptorInDataMap(query, map)) {
if (hasDuplicateQueryDescriptorInDataMap(query, map)) {
addFailure(validationResult, query, "Duplicate query name: %s", name);
return;
}
Expand All @@ -71,14 +86,21 @@ void validateName(QueryDescriptor query, ValidationResult validationResult) {
}

if (hasDuplicateQueryDescriptorInDataMap(query, nextMap)) {
addFailure(validationResult, query,
"Duplicate %s name in another DataMap: %s",
addFailure(validationResult, query, "Duplicate %s name in another DataMap: %s",
query.getType(), name);
return;
}
}
}

void checkForMultiCacheGroup(T query, ValidationResult validationResult) {
String cacheGroup = query.getProperty(QueryMetadata.CACHE_GROUPS_PROPERTY);
if (cacheGroup != null && cacheGroup.contains(",")) {
addFailure(validationResult, query, "Invalid cache group '%s', multiple groups are deprecated",
cacheGroup);
}
}

private boolean hasDuplicateQueryDescriptorInDataMap(QueryDescriptor queryDescriptor, DataMap dataMap) {
for (final QueryDescriptor otherQuery : dataMap.getQueryDescriptors()) {
if (otherQuery == queryDescriptor) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,82 @@
****************************************************************/
package org.apache.cayenne.project.validation;

import org.apache.cayenne.configuration.ConfigurationNode;
import org.apache.cayenne.validation.SimpleValidationFailure;
import org.apache.cayenne.validation.ValidationFailure;
import org.apache.cayenne.validation.ValidationResult;

import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
* A base superclass of various node validators.
*
*
* @since 3.1
*/
public abstract class ConfigurationNodeValidator {
public abstract class ConfigurationNodeValidator<T extends ConfigurationNode> {

protected final Supplier<ValidationConfig> configSupplier;

/**
* @param configSupplier the config defining the behavior of this validator.
* @since 5.0
*/
public ConfigurationNodeValidator(Supplier<ValidationConfig> configSupplier) {
this.configSupplier = configSupplier;
}

public void addFailure(
ValidationResult validationResult,
Object source,
String messageFormat,
Object... messageParameters) {
/**
* @param node the node that needs to be validated.
* @param validationResult the appendable validation result.
* @since 5.0
*/
public abstract void validate(T node, ValidationResult validationResult);

public void addFailure(ValidationResult validationResult, T source, String messageFormat,
Object... messageParameters) {
String message = String.format(messageFormat, messageParameters);
validationResult.addFailure(new SimpleValidationFailure(source, message));
validationResult.addFailure(new ProjectValidationFailure(source, message));
}

public void addFailure(ValidationResult validationResult, SimpleValidationFailure failure) {
validationResult.addFailure(failure);
}

public void addFailure(
ValidationResult validationResult,
SimpleValidationFailure failure) {
validationResult.addFailure(failure);

protected Performer<T> on(T node, ValidationResult validationResult) {
return new Performer<>(node, validationResult);
}

protected class Performer<N> {

private final N node;
private final ValidationResult validationResult;

protected Performer(N node, ValidationResult validationResult) {
this.node = node;
this.validationResult = validationResult;
}

protected Performer<N> performIfEnabled(Inspection inspection, BiConsumer<N, ValidationResult> action) {
return performIfEnabled(inspection, () -> action.accept(node, validationResult));
}

protected Performer<N> performIfEnabled(Inspection inspection, Runnable action) {
if (configSupplier.get().isEnabled(inspection)) {
performAndMarkFailures(inspection, action);
}
return this;
}

private void performAndMarkFailures(Inspection inspection, Runnable action) {
List<ValidationFailure> failuresBefore = new ArrayList<>(validationResult.getFailures());
action.run();
validationResult.getFailures().stream()
.filter(Predicate.not(failuresBefore::contains))
.forEach(failure -> ((ProjectValidationFailure) failure).setInspection(inspection));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,25 @@
import org.apache.cayenne.util.Util;
import org.apache.cayenne.validation.ValidationResult;

class DataChannelValidator extends ConfigurationNodeValidator {
import java.util.function.Supplier;

void validate(DataChannelDescriptor domain, ValidationResult validationResult) {
class DataChannelValidator extends ConfigurationNodeValidator<DataChannelDescriptor> {

/**
* @param configSupplier the config defining the behavior of this validator.
* @since 5.0
*/
public DataChannelValidator(Supplier<ValidationConfig> configSupplier) {
super(configSupplier);
}

@Override
public void validate(DataChannelDescriptor node, ValidationResult validationResult) {
on(node, validationResult)
.performIfEnabled(Inspection.DATA_CHANNEL_NO_NAME, this::checkForName);
}

private void checkForName(DataChannelDescriptor domain, ValidationResult validationResult) {
String name = domain.getName();
if (Util.isEmptyString(name)) {
addFailure(validationResult, domain, "Unnamed DataDomain");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,45 +24,38 @@
import org.apache.cayenne.util.Util;
import org.apache.cayenne.validation.ValidationResult;

class DataMapValidator extends ConfigurationNodeValidator {
import java.util.function.Supplier;

void validate(DataMap map, ValidationResult validationResult) {
validateName(map, validationResult);
validateNodeLinks(map, validationResult);
validateJavaPackage(map, validationResult);
}

private void validateNodeLinks(DataMap map, ValidationResult validationResult) {
DataChannelDescriptor domain = map.getDataChannelDescriptor();
if (domain == null) {
return;
}
class DataMapValidator extends ConfigurationNodeValidator<DataMap> {

boolean unlinked = true;
int nodeCount = 0;
for (DataNodeDescriptor node : domain.getNodeDescriptors()) {
nodeCount++;
if (node.getDataMapNames().contains(map.getName())) {
unlinked = false;
break;
}
}
/**
* @param configSupplier the config defining the behavior of this validator.
* @since 5.0
*/
public DataMapValidator(Supplier<ValidationConfig> configSupplier) {
super(configSupplier);
}

if (unlinked && nodeCount > 0) {
addFailure(validationResult, map, "DataMap is not linked to any DataNodes");
}
@Override
public void validate(DataMap node, ValidationResult validationResult) {
on(node, validationResult)
.performIfEnabled(Inspection.DATA_MAP_NO_NAME, this::checkForName)
.performIfEnabled(Inspection.DATA_MAP_NAME_DUPLICATE, this::checkForNameDuplicates)
.performIfEnabled(Inspection.DATA_MAP_NODE_LINKAGE, this::checkForNodeLinkage)
.performIfEnabled(Inspection.DATA_MAP_JAVA_PACKAGE, this::validateJavaPackage);
}

private void validateName(DataMap map, ValidationResult validationResult) {
private void checkForName(DataMap map, ValidationResult validationResult) {
String name = map.getName();

if (Util.isEmptyString(name)) {
addFailure(validationResult, map, "Unnamed DataMap");
return;
}
}

private void checkForNameDuplicates(DataMap map, ValidationResult validationResult) {
String name = map.getName();
DataChannelDescriptor domain = map.getDataChannelDescriptor();
if (domain == null) {
if (domain == null || Util.isEmptyString(name)) {
return;
}

Expand All @@ -71,25 +64,44 @@ private void validateName(DataMap map, ValidationResult validationResult) {
if (otherMap == map) {
continue;
}

if (name.equals(otherMap.getName())) {
addFailure(validationResult, map, "Duplicate DataMap name: %s", name);
return;
}
}
}

private void checkForNodeLinkage(DataMap map, ValidationResult validationResult) {
DataChannelDescriptor domain = map.getDataChannelDescriptor();
if (domain == null) {
return;
}

boolean linked = false;
int nodeCount = 0;
for (DataNodeDescriptor node : domain.getNodeDescriptors()) {
nodeCount++;
if (node.getDataMapNames().contains(map.getName())) {
linked = true;
break;
}
}

if (!linked && nodeCount > 0) {
addFailure(validationResult, map, "DataMap is not linked to any DataNodes");
}
}

private void validateJavaPackage(DataMap map, ValidationResult validationResult) {
String javaPackage = map.getDefaultPackage();

if(Util.isEmptyString(javaPackage)) {
if (Util.isEmptyString(javaPackage)) {
addFailure(validationResult, map, "Java package is not set in DataMap '%s'", map.getName());
return;
}

NameValidationHelper helper = NameValidationHelper.getInstance();
String invalidChars = helper.invalidCharsInJavaClassName(javaPackage);
if(invalidChars != null) {
if (invalidChars != null) {
addFailure(validationResult, map, "DataMap '%s' Java package '%s' contains invalid characters: %s",
map.getName(), javaPackage, invalidChars);
}
Expand Down
Loading