All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sap.cds.impl.Cascader Maven / Gradle / Ivy

The newest version!
/************************************************************************
 * © 2021-2022 SAP SE or an SAP affiliate company. All rights reserved. *
 ************************************************************************/
package com.sap.cds.impl;

import static com.sap.cds.util.CdsModelUtils.isCascading;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sap.cds.impl.parser.token.RefSegmentBuilder;
import com.sap.cds.ql.CQL;
import com.sap.cds.ql.Predicate;
import com.sap.cds.ql.StructuredType;
import com.sap.cds.ql.cqn.CqnExistsSubquery;
import com.sap.cds.ql.cqn.CqnPredicate;
import com.sap.cds.ql.cqn.CqnStructuredTypeRef;
import com.sap.cds.ql.cqn.CqnVisitor;
import com.sap.cds.reflect.CdsAssociationType;
import com.sap.cds.reflect.CdsElement;
import com.sap.cds.reflect.CdsEntity;
import com.sap.cds.util.CdsModelUtils.CascadeType;

public class Cascader {
	private static final Logger logger = LoggerFactory.getLogger(Cascader.class);
	private final CascadeType cascadeType;
	private final CdsEntity entity;
	private final Set visited = new HashSet<>();
	private final LinkedList> paths = new LinkedList<>();
	private CqnStructuredTypeRef ref;
	private CqnPredicate filter;
	private boolean unsupported = false;

	private Cascader(CascadeType cascadeType, CdsEntity entity) {
		this.cascadeType = cascadeType;
		this.entity = entity;
	}

	public static Cascader create(CascadeType cascadeType, CdsEntity entity) {
		return new Cascader(cascadeType, entity);
	}

	public Cascader from(String path) {
		return from(path != null ? CQL.to(path).asRef() : null);
	}

	public Cascader from(CqnStructuredTypeRef ref) {
		this.ref = ref;
		return this;
	}

	public Cascader where(Optional filter) {
		filter.ifPresent(f -> f.accept(new CqnVisitor() {
			@Override
			public void visit(CqnExistsSubquery query) {
				logger.debug("Cascading delete on {} cannot be optimized due to condition: {}",
						entity.getQualifiedName(), f);
				unsupported = true;
			}
		}));
		this.filter = filter.orElse(null);
		return this;
	}

	public boolean cascade(Consumer> action) {
		if (unsupported) {
			return false;
		}
		StructuredType path = ref != null ? CQL.to(RefSegmentBuilder.copy(ref.segments())) : null;
		if (path != null && filter != null) {
			path.filter(ref.targetSegment().filter().map(f -> CQL.and(f, filter)).orElse((Predicate) filter));
		}
		boolean acyclic = cascade(path, entity);
		if (acyclic) {
			paths.forEach(action::accept);
		}
		return acyclic;
	}

	private boolean cascade(StructuredType path, CdsEntity entity) {
		return entity.associations().filter(a -> isCascading(cascadeType, a))
				.allMatch(assoc -> cascade(path, assoc));
	}

	private boolean cascade(StructuredType path, CdsElement association) {
		CdsAssociationType assocType = association.getType();
		CdsEntity target = assocType.getTarget();
		if (!visited.add(association.getQualifiedName())) {
			logger.debug("Cascading delete on {} cannot be optimized due to cycle in delete graph: {}",
					entity.getQualifiedName(), association.getQualifiedName());
			return false;
		}
		StructuredType p = path != null ? path.to(association.getName()) : CQL.to(association.getName());
		paths.addFirst(p);
		return cascade(p, target);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy