use of org.jboss.hal.dmr.ModelDescriptionConstants.ALTERNATIVES in project console by hal.
the class OperationFactory method fromChangeSet.
/**
* Turns a change-set into a composite operation containing
* {@linkplain org.jboss.hal.dmr.ModelDescriptionConstants#WRITE_ATTRIBUTE_OPERATION write-attribute} and
* {@linkplain org.jboss.hal.dmr.ModelDescriptionConstants#UNDEFINE_ATTRIBUTE_OPERATION undefine-attribute} operations.
* <p>
* The composite operation will contain {@linkplain org.jboss.hal.dmr.ModelDescriptionConstants#UNDEFINE_ATTRIBUTE_OPERATION
* undefine-attribute} operations which reflect the alternative attributes as defined in the specified metadata.
*
* @param address the fq address used for the operations
* @param changeSet the changed values
* @param metadata the metadata which should contain the attribute definitions of the change-set
*/
public Composite fromChangeSet(ResourceAddress address, Map<String, Object> changeSet, Metadata metadata) {
// TODO Is it safe to always use ATTRIBUTES as path when calling ResourceDescription methods?
Map<String, Operation> operations = new HashMap<>();
HashMap<String, Object> localChanges = new HashMap<>(changeSet);
ResourceDescription resourceDescription = metadata.getDescription();
// look for alternatives
Set<String> conflicts = new HashSet<>();
Map<String, List<String>> allAlternatives = localChanges.keySet().stream().filter(name -> {
Object value = changeSet.get(name);
return !isNullOrEmpty(value);
}).collect(toMap(identity(), name -> resourceDescription.findAlternatives(ATTRIBUTES, name)));
allAlternatives.forEach((attribute, alternatives) -> {
logger.debug("Alternatives resolution for {} -> [{}]", attribute, String.join(", ", alternatives));
HashSet<String> intersection = new HashSet<>(alternatives);
intersection.retainAll(changeSet.keySet());
if (intersection.isEmpty()) {
// the easy part: no conflicts
alternatives.forEach(alternative -> {
boolean alternativeDoesntExist = resourceDescription.findAttribute(ATTRIBUTES, alternative) == null;
if (resourceDescription.isDeprecated(ATTRIBUTES, alternative) || alternativeDoesntExist) {
logger.debug("Skip undefine operations for deprecated or non-existent alternative {}", alternative);
} else {
logger.debug("Add undefine operations for alternative {}", alternative);
operations.putIfAbsent(alternative, undefineAttribute(address, alternative));
List<String> requires = resourceDescription.findRequires(ATTRIBUTES, alternative);
if (!requires.isEmpty()) {
logger.debug("Add undefine operations for attributes which require {}: [{}]", alternative, String.join(", ", requires));
requires.forEach(r -> operations.putIfAbsent(r, undefineAttribute(address, r)));
}
}
});
} else {
// possible conflicts: one or more alternatives are also in the change-set
// just collect for now and resolve later
conflicts.add(attribute);
conflicts.addAll(intersection);
logger.debug("Record conflict {} <-> [{}]", attribute, String.join(", ", intersection));
}
alternatives.forEach(localChanges::remove);
});
if (!conflicts.isEmpty()) {
// try to resolve conflicts: only one of the conflicting attributes must have a value other than
// null, empty or default
logger.debug("Try to resolve conflicts between alternatives [{}]", String.join(", ", conflicts));
Map<Boolean, List<String>> resolution = conflicts.stream().collect(groupingBy(conflict -> {
Object value = changeSet.get(conflict);
return isNullOrEmpty(value) || resourceDescription.isDefaultValue(ATTRIBUTES, conflict, value);
}));
List<String> undefine = resolution.getOrDefault(true, Collections.emptyList());
List<String> write = resolution.getOrDefault(false, Collections.emptyList());
if (write.size() > 1) {
logger.error("More than one conflicting alternative attribute which is not null, empty or default: [{}]. This should have been caught by a form validation. Adding the write operations anyway to get an appropriate error message from the server.", String.join(", ", write));
}
logger.debug("Add undefine operations for [{}], write operation for [{}]", String.join(", ", undefine), String.join(", ", write));
undefine.forEach(u -> {
operations.putIfAbsent(u, undefineAttribute(address, u));
localChanges.remove(u);
// process requires of the current undefine attribute
List<String> requires = resourceDescription.findRequires(ATTRIBUTES, u);
requires.forEach(ur -> {
operations.putIfAbsent(ur, undefineAttribute(address, ur));
localChanges.remove(ur);
});
});
write.forEach(w -> {
operations.putIfAbsent(w, writeAttribute(address, w, changeSet.get(w), resourceDescription, true));
localChanges.remove(w);
List<String> writeAlternatives = resourceDescription.findAlternatives(ATTRIBUTES, w);
// process alternatives of the current write attribute
writeAlternatives.forEach(wa -> {
operations.putIfAbsent(wa, undefineAttribute(address, wa));
localChanges.remove(wa);
});
});
}
// handle the remaining attributes
logger.debug("Process remaining attributes [{}]", String.join(", ", localChanges.keySet()));
localChanges.forEach((name, value) -> operations.putIfAbsent(name, writeAttribute(address, name, value, resourceDescription, false)));
return new Composite(operations.values().stream().filter(Objects::nonNull).collect(toList()));
}
Aggregations