Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.sap.cds.impl.DataProcessor Maven / Gradle / Ivy
/************************************************************************
* © 2020-2024 SAP SE or an SAP affiliate company. All rights reserved. *
************************************************************************/
package com.sap.cds.impl;
import static java.util.Arrays.asList;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import com.google.common.collect.Lists;
import com.sap.cds.CdsDataProcessor;
import com.sap.cds.ql.cqn.Path;
import com.sap.cds.ql.impl.PathImpl;
import com.sap.cds.reflect.CdsArrayedType;
import com.sap.cds.reflect.CdsAssociationType;
import com.sap.cds.reflect.CdsBaseType;
import com.sap.cds.reflect.CdsElement;
import com.sap.cds.reflect.CdsSimpleType;
import com.sap.cds.reflect.CdsStructuredType;
import com.sap.cds.reflect.CdsType;
import com.sap.cds.util.CdsModelUtils;
import com.sap.cds.util.CdsModelUtils.CascadeType;
public class DataProcessor implements CdsDataProcessor {
private final List actions = new ArrayList<>();
private CascadeType cascadeType;
private boolean depthFirst;
public static class Factory implements CdsDataProcessor.Factory {
@Override
public CdsDataProcessor create() {
return DataProcessor.create();
}
}
public static DataProcessor create() {
return new DataProcessor();
}
public DataProcessor forInsert() {
cascadeType = CascadeType.INSERT;
return this;
}
public DataProcessor forUpdate() {
cascadeType = CascadeType.UPDATE;
return this;
}
public DataProcessor withDepthFirst() {
depthFirst = true;
return this;
}
public CdsDataProcessor action(Action action) {
actions.add(action);
return this;
}
public DataProcessor action(BiConsumer> action) {
actions.add(new Action() {
@Override
public void entry(Path path, CdsElement unused, CdsStructuredType type, Map entry) {
action.accept(type, entry);
}
});
return this;
}
public DataProcessor bulkAction(BiConsumer>> action) {
actions.add(new Action() {
@Override
public void entries(Path path, CdsElement unused, CdsStructuredType type,
Iterable> entries) {
action.accept(type, entries);
}
});
return this;
}
@Override
public DataProcessor addConverter(Filter filter, Converter valConverter) {
actions.add(new Action() {
@Override
public void entries(Path path2e, CdsElement e, CdsStructuredType type,
Iterable> entries) {
forAllEntries(path(path2e, e, type), entries, type, filter, (element, entry) -> {
if (entry.containsKey(element.getName())) {
Path path = path(path2e, e, type, entry);
String name = element.getName();
Object value = entry.get(name);
convert(path, element, entry, name, value, valConverter);
}
});
}
private void convert(Path path, CdsElement element, Map entry, String name, Object value,
Converter converter) {
if (value instanceof List list) {
ListIterator iterator = list.listIterator();
while (iterator.hasNext()) {
Object val = converter.convert(path, element, iterator.next());
if (val == Converter.REMOVE) {
iterator.remove();
} else {
iterator.set(val);
}
}
} else {
Object val = converter.convert(path, element, entry.get(name));
if (val == Converter.REMOVE) {
entry.remove(name);
} else {
entry.put(name, val);
}
}
}
@Override
public void array(Path path, CdsElement element, CdsSimpleType type, List items) {
if (filter.test(path, element, type)) {
for (int i = 0; i < items.size(); i++) {
items.set(i, valConverter.convert(path, element, items.get(i)));
}
}
}
});
return this;
}
@Override
public DataProcessor addGenerator(Filter filter, Generator valGenerator) {
actions.add(new Action() {
@Override
public void entries(Path path, CdsElement element, CdsStructuredType type,
Iterable> entries) {
type.concreteNonAssociationElements().filter(e -> filter.test(path(path, element, type), e)) //
.forEach(e -> entries.forEach(entry -> {
String name = e.getName();
if (entry.get(name) == null) {
entry.put(name, valGenerator.generate(path(path, element, type, entry), e,
entry.containsKey(name)));
}
}));
}
});
return this;
}
@Override
public DataProcessor addValidator(Filter filter, Validator validator, Mode mode) {
actions.add(new Action() {
final Handler handler = validationHandler(mode);
@Override
public void entries(Path path2e, CdsElement e, CdsStructuredType type,
Iterable> entries) {
forAllEntries(path(path2e, e, type), entries, type, filter, (element, entry) -> {
Path path = path(path2e, e, type, entry);
handler.apply(entry, element.getName(), value -> validate(path, element, value, validator));
});
}
@SuppressWarnings("unchecked")
private void validate(Path path, CdsElement element, Object value, Validator validator) {
if (value instanceof List list) {
list.forEach(v -> validator.validate(path, element, v));
} else {
validator.validate(path, element, value);
}
}
@Override
public void array(Path path, CdsElement element, CdsSimpleType type, List items) {
if (filter.test(path, element, type)) {
items.forEach(value -> validator.validate(path, element, value));
}
}
});
return this;
}
@FunctionalInterface
private static interface Handler {
void apply(Map map, String key, Consumer processor);
}
private static Handler validationHandler(Mode mode) {
switch (mode) {
case DECLARED:
return (map, key, consumer) -> {
Object value = map.getOrDefault(key, CdsDataProcessor.ABSENT);
consumer.accept(value);
};
case NOT_NULL:
return (map, key, consumer) -> {
Object value = map.get(key);
if (value != null) {
consumer.accept(value);
}
};
case NULL:
return (map, key, consumer) -> {
Object value = map.getOrDefault(key, CdsDataProcessor.ABSENT);
if (value == null || value == CdsDataProcessor.ABSENT) {
consumer.accept(value);
}
};
default: // PRESENT
return (map, key, consumer) -> {
if (map.containsKey(key)) {
consumer.accept(map.get(key));
}
};
}
}
private static Path path(Path path, CdsElement e, CdsStructuredType type) {
return ((PathImpl) path).append(e, type, new HashMap<>());
}
private static Path path(Path path, CdsElement e, CdsStructuredType type, Map entry) {
return ((PathImpl) path).append(e, type, entry);
}
private static void forAllEntries(Path path, Iterable> entries, CdsStructuredType type,
Filter filter, BiConsumer> handler) {
type.elements().filter(e -> filter.test(path, e, e.getType())).forEach(element -> entries.forEach(entry -> {
handler.accept(element, entry);
}));
}
@Override
public void process(Map entry, CdsStructuredType entryType) {
process(asList(entry), entryType);
}
@Override
@SuppressWarnings("unchecked")
public void process(Iterable extends Map> entries, CdsStructuredType entryType) {
if (entryType == null) {
throw new IllegalArgumentException("Entry type must not be null");
}
Path path = new PathImpl(new LinkedList<>());
executeAndTraverse(() -> performActions(path, null, entryType, (Iterable>) entries),
() -> traverseEntries(path, null, entryType, entries));
}
private void performActions(Path path, CdsElement element, CdsStructuredType type,
Iterable> entries) {
actions.stream().forEach(action -> action.entries(path, element, type, entries));
}
private void performActions(Path path, CdsElement element, CdsStructuredType type, Map entry) {
actions.stream().forEach(action -> action.entry(path, element, type, entry));
}
private void performActions(Path path, CdsElement element, CdsSimpleType type, List items) {
for (Action action : actions) {
action.array(path, element, type, items);
}
}
@SuppressWarnings("unchecked")
private void traverseEntries(Path path, CdsElement element, CdsStructuredType struct,
Iterable extends Map> entries) {
entries.forEach(entry -> entry.forEach((key, value) -> {
if (value instanceof List list) {
struct.findElement(key).filter(this::cascade)
.ifPresent(e -> traverseMany(path(path, element, struct, entry), e, list));
} else if (value instanceof Map) {
struct.findElement(key).filter(this::cascade).ifPresent(
e -> traverseOne(path(path, element, struct, entry), e, (Map) value));
}
}));
}
private boolean cascade(CdsElement e) {
return !e.getType().isSimpleType(CdsBaseType.MAP)
&& (cascadeType == null || !e.getType().isAssociation() || CdsModelUtils.isCascading(cascadeType, e));
}
private void traverseOne(Path path, CdsElement element, Map entry) {
CdsStructuredType struct = struct(element);
executeAndTraverse(() -> performActions(path, element, struct, entry),
() -> traverseEntries(path, element, struct, asList(entry)));
}
@SuppressWarnings("unchecked")
private void traverseMany(Path path, CdsElement element, List> entries) {
CdsType type = type(element);
if (type.isSimple()) {
performActions(path, element, type.as(CdsSimpleType.class), (List) entries);
} else {
CdsStructuredType struct = type.as(CdsStructuredType.class);
List> structEntries = (List>) entries;
executeAndTraverse(() -> performActions(path, element, struct, structEntries),
() -> traverseEntries(path, element, struct, structEntries));
}
}
private void executeAndTraverse(Runnable execution, Runnable traversal) {
if (depthFirst) {
traversal.run();
execution.run();
} else {
execution.run();
traversal.run();
}
}
private static CdsStructuredType struct(CdsElement element) {
CdsType type = element.getType();
if (type.isAssociation()) {
return type.as(CdsAssociationType.class).getTarget();
}
return type.as(CdsStructuredType.class);
}
private static CdsType type(CdsElement element) {
CdsType type = element.getType();
if (type.isStructured() || type.isAssociation()) {
return struct(element);
}
if (type.isArrayed()) {
return type.as(CdsArrayedType.class).getItemsType();
}
return type; // map type
}
public interface Action {
default void entry(Path path, CdsElement element, CdsStructuredType type, Map entry) {
entries(path, element, type, Lists.newArrayList(entry));
}
default void entries(Path path, CdsElement element, CdsStructuredType type,
Iterable> entries) {
entries.forEach(entry -> entry(path, element, type, entry));
}
default void array(Path path, CdsElement element, CdsSimpleType type, List items) {
}
}
}