org.eclipse.viatra.query.runtime.matchers.psystem.rewriters.PBodyNormalizer.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.viatra.query.runtime.matchers.psystem.rewriters.PBodyNormalizer.java

Source

/*******************************************************************************
 * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Gabor Bergmann - initial API and implementation
 *******************************************************************************/

package org.eclipse.viatra.query.runtime.matchers.psystem.rewriters;

import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Set;

import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
import org.eclipse.viatra.query.runtime.matchers.context.IQueryMetaContext;
import org.eclipse.viatra.query.runtime.matchers.planning.QueryProcessingException;
import org.eclipse.viatra.query.runtime.matchers.planning.helpers.TypeHelper;
import org.eclipse.viatra.query.runtime.matchers.psystem.ITypeConstraint;
import org.eclipse.viatra.query.runtime.matchers.psystem.ITypeInfoProviderConstraint;
import org.eclipse.viatra.query.runtime.matchers.psystem.PBody;
import org.eclipse.viatra.query.runtime.matchers.psystem.PConstraint;
import org.eclipse.viatra.query.runtime.matchers.psystem.TypeJudgement;
import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Equality;
import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Inequality;
import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PDisjunction;
import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery;
import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery.PQueryStatus;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;

/**
 * A disjunction rewriter for creating a normalized form of specification, unifying variables and running basic sanity
 * checks. This rewriter does not copy but modifies directly the original specification, requiring a mutable
 * disjunction.
 * 
 * @author Gabor Bergmann
 * 
 */
public class PBodyNormalizer extends PDisjunctionRewriter {

    private IQueryMetaContext context;

    public PBodyNormalizer(IQueryMetaContext context) {
        this.context = context;
    }

    /**
     * Returns whether unary constraint elimination is enabled. This behavior can be customized by creating a subclass
     * with a custom implementation.
     * 
     * @since 1.6
     */
    protected boolean shouldCalculateImpliedTypes(PQuery query) {
        return true;
    }

    /**
     * Returns whether 'weakened alternative' suggestions of the context shall be expanded as additional PConstraints. 
     * This behavior can be customized by creating a subclass
     * with a custom implementation.
     * 
     * @since 1.6
     */
    protected boolean shouldExpandWeakenedAlternatives(PQuery query) {
        return false;
    }

    @Override
    public PDisjunction rewrite(PDisjunction disjunction) {
        Set<PBody> normalizedBodies = new LinkedHashSet<>();
        for (PBody body : disjunction.getBodies()) {
            PBodyCopier copier = new PBodyCopier(body, getTraceCollector());
            PBody modifiedBody = copier.getCopiedBody();
            normalizeBody(modifiedBody);
            normalizedBodies.add(modifiedBody);
            modifiedBody.setStatus(PQueryStatus.OK);
        }
        return new PDisjunction(normalizedBodies);
    }

    public void setContext(IQueryMetaContext context) {
        this.context = context;
    }

    /**
     * Provides a normalized version of the pattern body. May return a different version than the original version if
     * needed.
     * 
     * @param body
     */
    public PBody normalizeBody(PBody body) {
        try {
            return normalizeBodyInternal(body);
        } catch (QueryProcessingException e) {
            throw new RewriterException("Error during rewriting: {1}", new String[] { e.getMessage() },
                    e.getShortMessage(), body.getPattern(), e);
        }
    }

    PBody normalizeBodyInternal(PBody body) {
        // UNIFICATION AND WEAK INEQUALITY ELIMINATION
        unifyVariablesAlongEqualities(body);
        eliminateWeakInequalities(body);
        removeMootEqualities(body);

        // ADDING WEAKENED ALTERNATIVES
        if (shouldExpandWeakenedAlternatives(body.getPattern())) {
            expandWeakenedAlternativeConstraints(body);
        }

        // CONSTRAINT ELIMINATION WITH TYPE INFERENCE
        if (shouldCalculateImpliedTypes(body.getPattern())) {
            eliminateInferrableTypes(body, context);
        } else {
            // ELIMINATE DUPLICATE TYPE CONSTRAINTS
            eliminateDuplicateTypeConstraints(body);
        }

        // PREVENTIVE CHECKS
        checkSanity(body);
        return body;
    }

    private void removeMootEqualities(PBody body) {
        Set<Equality> equals = body.getConstraintsOfType(Equality.class);
        for (Equality equality : equals) {
            if (equality.isMoot()) {
                equality.delete();
                derivativeRemoved(equality, ConstraintRemovalReason.MOOT_EQUALITY);
            }
        }
    }

    /**
     * Unifies allVariables along equalities so that they can be handled as one.
     * 
     * @param body
     */
    void unifyVariablesAlongEqualities(PBody body) {
        Set<Equality> equals = body.getConstraintsOfType(Equality.class);
        for (Equality equality : equals) {
            if (!equality.isMoot()) {
                equality.getWho().unifyInto(equality.getWithWhom());
            }
        }
    }

    /**
     * Eliminates weak inequalities if they are not substantiated.
     * 
     * @param body
     */
    void eliminateWeakInequalities(PBody body) {
        for (Inequality inequality : body.getConstraintsOfType(Inequality.class)) {
            if (inequality.isEliminable()) {
                inequality.eliminateWeak();
                derivativeRemoved(inequality, ConstraintRemovalReason.WEAK_INEQUALITY_SELF_LOOP);
            }
        }
    }

    /**
     * Eliminates all type constraints that are inferrable from other constraints.
     */
    void eliminateInferrableTypes(final PBody body, IQueryMetaContext context) {
        Set<TypeJudgement> subsumedByRetainedConstraints = new HashSet<TypeJudgement>();
        LinkedList<ITypeConstraint> allTypeConstraints = new LinkedList<ITypeConstraint>();
        for (PConstraint pConstraint : body.getConstraints()) {
            if (pConstraint instanceof ITypeConstraint) {
                allTypeConstraints.add((ITypeConstraint) pConstraint);
            } else if (pConstraint instanceof ITypeInfoProviderConstraint) {
                // non-type constraints are all retained
                final Set<TypeJudgement> directJudgements = ((ITypeInfoProviderConstraint) pConstraint)
                        .getImpliedJudgements(context);
                subsumedByRetainedConstraints = TypeHelper.typeClosure(subsumedByRetainedConstraints,
                        directJudgements, context);
            }
        }
        Comparator<ITypeConstraint> eliminationOrder = (o1, o2) -> {
            IInputKey type1 = o1.getEquivalentJudgement().getInputKey();
            IInputKey type2 = o2.getEquivalentJudgement().getInputKey();

            int result = context.getSuggestedEliminationOrdering().compare(type1, type2);
            return (result == 0) ? PConstraint.COMPARE_BY_MONOTONOUS_ID.compare(o1, o2) : result;
        };

        Collections.sort(allTypeConstraints, eliminationOrder);
        Queue<ITypeConstraint> potentialConstraints = allTypeConstraints; // rename for better comprehension

        while (!potentialConstraints.isEmpty()) {
            ITypeConstraint candidate = potentialConstraints.poll();

            boolean isSubsumed = subsumedByRetainedConstraints.contains(candidate.getEquivalentJudgement());
            if (!isSubsumed) {
                Set<TypeJudgement> typeClosure = subsumedByRetainedConstraints;
                for (ITypeConstraint subsuming : potentialConstraints) { // the remaining ones
                    final Set<TypeJudgement> directJudgements = subsuming.getImpliedJudgements(context);
                    typeClosure = TypeHelper.typeClosure(typeClosure, directJudgements, context);

                    if (typeClosure.contains(candidate.getEquivalentJudgement())) {
                        isSubsumed = true;
                        break;
                    }
                }
            }
            if (isSubsumed) { // eliminated
                candidate.delete();
                derivativeRemoved(candidate, ConstraintRemovalReason.TYPE_SUBSUMED);
            } else { // retained
                subsumedByRetainedConstraints = TypeHelper.typeClosure(subsumedByRetainedConstraints,
                        candidate.getImpliedJudgements(context), context);
            }
        }
    }

    /**
     * Inserts "weakened alternative" constraints suggested by the meta context that aid in coming up with a query plan.
     */
    void expandWeakenedAlternativeConstraints(PBody body) {
        Set<TypeJudgement> allJudgements = new HashSet<TypeJudgement>();
        Set<TypeJudgement> newJudgementsToAdd = new HashSet<TypeJudgement>();
        Queue<TypeJudgement> judgementsToProcess = new LinkedList<TypeJudgement>();
        Multimap<TypeJudgement, PConstraint> traceability = HashMultimap.create();

        for (ITypeConstraint typeConstraint : body.getConstraintsOfType(ITypeConstraint.class)) {
            TypeJudgement equivalentJudgement = typeConstraint.getEquivalentJudgement();
            judgementsToProcess.add(equivalentJudgement);
            allJudgements.add(equivalentJudgement);
            traceability.put(equivalentJudgement, typeConstraint);
        }

        while (!judgementsToProcess.isEmpty()) {
            TypeJudgement judgement = judgementsToProcess.poll();
            for (TypeJudgement alternativeJudgement : judgement.getWeakenedAlternativeJudgements(context)) {
                if (allJudgements.add(alternativeJudgement)) {
                    newJudgementsToAdd.add(alternativeJudgement);
                    judgementsToProcess.add(alternativeJudgement);
                    traceability.putAll(alternativeJudgement, traceability.get(judgement));
                }
            }
        }

        for (TypeJudgement typeJudgement : newJudgementsToAdd) {
            PConstraint newConstraint = typeJudgement.createConstraintFor(body);
            for (PConstraint source : traceability.get(typeJudgement)) {
                addTrace(source, newConstraint);
            }
        }
    }

    private Object getConstraintKey(PConstraint constraint) {
        if (constraint instanceof ITypeConstraint) {
            return ((ITypeConstraint) constraint).getEquivalentJudgement();
        }
        // Do not check duplication for any other types
        return constraint;
    }

    void eliminateDuplicateTypeConstraints(PBody body) {
        Map<Object, PConstraint> constraints = new HashMap<>();
        for (PConstraint constraint : body.getConstraints()) {
            Object key = getConstraintKey(constraint);
            // Retain first found instance of a constraint
            if (!constraints.containsKey(key)) {
                constraints.put(key, constraint);
            }
        }

        // Retain collected constraints, remove everything else
        Iterator<PConstraint> iterator = body.getConstraints().iterator();
        Collection<PConstraint> toRetain = constraints.values();
        while (iterator.hasNext()) {
            PConstraint next = iterator.next();
            if (!toRetain.contains(next)) {
                derivativeRemoved(next, ConstraintRemovalReason.DUPLICATE);
                iterator.remove();
            }
        }
    }

    /**
     * Verifies the sanity of all constraints. Should be issued as a preventive check before layouting.
     * 
     * @param body
     * @throws RetePatternBuildException
     */
    void checkSanity(PBody body) {
        for (PConstraint pConstraint : body.getConstraints())
            pConstraint.checkSanity();
    }

}