/* * Copyright 2003-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jetbrains.mps.smodel.runtime; import jetbrains.mps.smodel.runtime.base.BaseConstraintsDescriptor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; /** * * @author Radimir.Sorokin * @since 3.5 */ public final class ConstraintFunctions { private ConstraintFunctions() { } /** * Creates a composition of constraint functions which returns {@code boolean} as result. * * Resulting constraint function returns true at only if all supplied constraint functions returns true. * If none constraint functions supplied, it always returns true. */ @NotNull public static <Context> ConstraintFunction<Context, Boolean> createBooleanComposition(Iterable<ConstraintFunction<Context, Boolean>> constraints) { List<ConstraintFunction<Context, Boolean>> constraintList = collectConstraints(constraints); if (constraintList.size() == 0) { return (ConstraintFunction<Context, Boolean>) EMPTY_BOOLEAN_COMPOSITION; } if (constraintList.size() == 1) { return constraintList.get(0); } return new BooleanCompositionConstraintFunction<>(constraintList); } /** * Creates a composition of constraint functions which returns {@code ReferenceScopeProvider} as result. * * TODO While a scope conjunction not implemented, it returns first not-null result of supplied constraint functions */ @NotNull public static <Context> ConstraintFunction<Context, ReferenceScopeProvider> createScopeProviderComposition( Iterable<ConstraintFunction<Context, ReferenceScopeProvider>> constraints ) { List<ConstraintFunction<Context, ReferenceScopeProvider>> constraintList = collectConstraints(constraints); if (constraintList.size() == 0) { return (ConstraintFunction<Context, ReferenceScopeProvider>) EMPTY_SCOPE_PROVIDER_COMPOSITION; } if (constraintList.size() == 1) { return constraintList.get(0); } return new ScopeProviderCompositionConstraintFunction<>(constraintList); } public static ConstraintFunction<ConstraintContext_CanBeChild, Boolean> getCanBeChildConstraintFunction(ConstraintsDescriptor cd) { if (cd instanceof BaseConstraintsDescriptor) { return ((BaseConstraintsDescriptor) cd).getCanBeChildConstraint(); } return cd::canBeChild; } public static ConstraintFunction<ConstraintContext_CanBeParent, Boolean> getCanBeParentConstraintFunction(ConstraintsDescriptor cd) { if (cd instanceof BaseConstraintsDescriptor) { return ((BaseConstraintsDescriptor) cd).getCanBeParentConstraint(); } return cd::canBeParent; } public static ConstraintFunction<ConstraintContext_CanBeRoot, Boolean> getCanBeRootConstraintFunction(ConstraintsDescriptor cd) { if (cd instanceof BaseConstraintsDescriptor) { return ((BaseConstraintsDescriptor) cd).getCanBeRootConstraint(); } return cd::canBeRoot; } public static ConstraintFunction<ConstraintContext_CanBeAncestor, Boolean> getCanBeAncestorConstraintFunction(ConstraintsDescriptor cd) { if (cd instanceof BaseConstraintsDescriptor) { return ((BaseConstraintsDescriptor) cd).getCanBeAncestorConstraint(); } return cd::canBeAncestor; } public static ConstraintFunction<ConstraintContext_DefaultScopeProvider, ReferenceScopeProvider> getDefaultScopeConstraintFunction(ConstraintsDescriptor cd) { if (cd instanceof BaseConstraintsDescriptor) { return ((BaseConstraintsDescriptor) cd).getDefaultScopeConstraint(); } return (constraintContext_defaultScopeProvider, checkingNodeContext) -> cd.getDefaultScopeProvider(); } // filtering out duplicated constraint functions private static <C, R> List<ConstraintFunction<C, R>> collectConstraints(Iterable<ConstraintFunction<C, R>> constraints) { LinkedHashSet<ConstraintFunction<C, R>> constraintSet = new LinkedHashSet<>(); constraints.forEach(constraintSet::add); return new ArrayList<>(constraintSet); } private static final ConstraintFunction<?, Boolean> EMPTY_BOOLEAN_COMPOSITION = (context, checkingNodeContext) -> true; private static final ConstraintFunction<?, ReferenceScopeProvider> EMPTY_SCOPE_PROVIDER_COMPOSITION = (context, checkingNodeContext) -> null; private static final ConstraintFunction<?, ?> ERROR_COMPOSITION = (context, checkingNodeContext) -> { throw new IllegalStateException("Constraint functions can not be composed"); }; private static final class BooleanCompositionConstraintFunction<Context> implements ConstraintFunction<Context, Boolean> { private final Iterable<ConstraintFunction<Context, Boolean>> myDirectParents; /*package*/ BooleanCompositionConstraintFunction(Iterable<ConstraintFunction<Context, Boolean>> directParents) { myDirectParents = directParents; } @NotNull @Override public Boolean invoke(@NotNull Context context, CheckingNodeContext checkingNodeContext) { for (ConstraintFunction<Context, Boolean> parent : myDirectParents) { if (!parent.invoke(context, checkingNodeContext)) { return false; } } return true; } } private static final class ScopeProviderCompositionConstraintFunction<Context> implements ConstraintFunction<Context, ReferenceScopeProvider> { private final Iterable<ConstraintFunction<Context, ReferenceScopeProvider>> myDirectParents; public ScopeProviderCompositionConstraintFunction(Iterable<ConstraintFunction<Context, ReferenceScopeProvider>> directParents) { myDirectParents = directParents; } @Override public ReferenceScopeProvider invoke(@NotNull Context context, @Nullable CheckingNodeContext checkingNodeContext) { for (ConstraintFunction<Context, ReferenceScopeProvider> parent : myDirectParents) { ReferenceScopeProvider scopeProvider = parent.invoke(context, checkingNodeContext); if (scopeProvider != null) { return scopeProvider; } } return null; } } }