com.sap.cds.util.CdsSearchUtils Maven / Gradle / Ivy
/*******************************************************************
* © 2020 SAP SE or an SAP affiliate company. All rights reserved. *
*******************************************************************/
package com.sap.cds.util;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.toList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
import com.google.common.collect.Streams;
import com.sap.cds.CdsException;
import com.sap.cds.impl.builder.model.ElementRefImpl;
import com.sap.cds.impl.parser.token.RefSegmentImpl;
import com.sap.cds.ql.ElementRef;
import com.sap.cds.ql.RefSegment;
import com.sap.cds.reflect.CdsAnnotatable;
import com.sap.cds.reflect.CdsAnnotation;
import com.sap.cds.reflect.CdsAssociationType;
import com.sap.cds.reflect.CdsBaseType;
import com.sap.cds.reflect.CdsElement;
import com.sap.cds.reflect.CdsEntity;
import com.sap.cds.reflect.CdsStructuredType;
import com.sap.cds.reflect.CdsType;
public class CdsSearchUtils {
private static final String DEFAULT_SEARCH_ELEMENT = "Search.defaultSearchElement";
private static final String CDS_SEARCH = "cds.search";
@Deprecated
private static final String SEARCH_CASCADE = "Search.cascade";
private static final boolean INCLUDE = true;
private static final boolean EXCLUDE = false;
private final CdsStructuredType targetType;
private CdsSearchUtils(CdsStructuredType targetType) {
this.targetType = targetType;
}
public static Collection> searchableElementRefs(CdsStructuredType targetType) {
Set> segments = searchableSegments(targetType);
return segments.stream().map(CdsSearchUtils::ref).collect(toList());
}
private static Set> searchableSegments(CdsStructuredType targetType) {
CdsSearchUtils utils = new CdsSearchUtils(targetType);
return utils.searchableRefs();
}
private Set> searchableRefs() {
Set> refs = new HashSet<>();
handleNonAssocElements(INCLUDE, refs::add);
if (refs.isEmpty()) {
defaultSearchElements().map(CdsSearchUtils::segments).forEach(refs::add);
}
handleAssociations(refs);
handleNonAssocElements(EXCLUDE, refs::remove);
return refs;
}
private void handleNonAssocElements(boolean include, Consumer> action) {
Stream> cdsSearch = pathsDeclaredByCdsSearch(include);
Stream> defaultSearch = elements().filter(annotatedWith(DEFAULT_SEARCH_ELEMENT, include))
.map(CdsSearchUtils::segments);
Streams.concat(cdsSearch, defaultSearch).filter(s -> !isAssoc(s)).forEach(action);
}
private void handleAssociations(Set> refs) {
Stream> cdsSearch = pathsDeclaredByCdsSearch(INCLUDE);
Stream> searchCascade = elements().filter(searchCascade(INCLUDE)).map(CdsSearchUtils::segments);
Streams.concat(cdsSearch, searchCascade).filter(this::isAssoc).forEach(addSearchableRefsTo(refs));
}
private Consumer> addSearchableRefsTo(Set> refs) {
return prefix -> {
CdsEntity target = type(prefix).as(CdsAssociationType.class).getTarget();
searchableSegments(target).forEach(path -> {
List ref = new ArrayList<>(prefix);
ref.addAll(path);
refs.add(ref);
});
};
}
private Stream> pathsDeclaredByCdsSearch(boolean include) {
return targetType.annotations().filter(cdsSearch(include)).map(CdsSearchUtils::segments);
}
private static Predicate super CdsAnnotation>> cdsSearch(boolean include) {
return a -> a.getName().startsWith(CDS_SEARCH) && a.getValue().equals(include);
}
private static String path(CdsAnnotation> a) {
String name = a.getName();
return name.substring(CDS_SEARCH.length() + 1, name.length());
}
private static List segments(CdsAnnotation> a) {
return segments(path(a));
}
private Stream elements() {
return targetType.concreteElements();
}
private Stream defaultSearchElements() {
return elements().filter(e -> e.getType().isSimpleType(CdsBaseType.STRING));
}
private CdsElement element(List segments) {
Iterator iter = segments.iterator();
CdsStructuredType current = targetType;
CdsElement element;
boolean isAssoc;
do {
String seg = iter.next();
element = current.getElement(seg);
isAssoc = element.getType().isAssociation();
if (isAssoc) {
current = current.getTargetOf(seg);
} else if (iter.hasNext()) {
throw new CdsException("Invalid path: " + segments);
}
} while (iter.hasNext());
return element;
}
private CdsType type(List segments) {
return element(segments).getType();
}
private boolean isAssoc(List s) {
return isAssoc(element(s));
}
private static boolean isAssoc(CdsElement e) {
return e.getType().isAssociation();
}
@Deprecated
private static Predicate super CdsElement> searchCascade(boolean include) {
return annotatedWith(SEARCH_CASCADE, include);
}
private static Predicate super CdsAnnotatable> annotatedWith(String annotationName, boolean include) {
return t -> t.findAnnotation(annotationName).filter(a -> a.getValue().equals(include)).isPresent();
}
private static ElementRef> ref(List segments) {
List ids = new ArrayList<>();
for (String id : segments) {
ids.add(RefSegmentImpl.refSegment(id));
}
return ElementRefImpl.element(ids);
}
private static List segments(CdsElement e) {
return singletonList(e.getName());
}
private static List segments(String path) {
return asList(path.split("\\."));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy