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

io.mindmaps.graql.internal.pattern.VarImpl Maven / Gradle / Ivy

/*
 * MindmapsDB - A Distributed Semantic Database
 * Copyright (C) 2016  Mindmaps Research Ltd
 *
 * MindmapsDB is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * MindmapsDB is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with MindmapsDB. If not, see .
 */

package io.mindmaps.graql.internal.pattern;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import io.mindmaps.concept.ResourceType;
import io.mindmaps.graql.ValuePredicate;
import io.mindmaps.graql.Var;
import io.mindmaps.graql.admin.*;
import io.mindmaps.graql.internal.pattern.property.*;
import io.mindmaps.graql.internal.util.CommonUtil;
import io.mindmaps.graql.internal.util.StringConverter;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Stream;

import static io.mindmaps.graql.Graql.eq;
import static io.mindmaps.graql.Graql.var;
import static io.mindmaps.graql.internal.util.CommonUtil.toImmutableSet;
import static io.mindmaps.util.ErrorMessage.CONFLICTING_PROPERTIES;
import static io.mindmaps.util.ErrorMessage.SET_GENERATED_VARIABLE_NAME;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toSet;

/**
 * Implementation of Var interface
 */
class VarImpl implements VarAdmin {

    private Set properties = new HashSet<>();

    private String name;
    private final boolean userDefinedName;

    /**
     * Create a variable with a random variable name
     */
    VarImpl() {
        this.name = UUID.randomUUID().toString();
        this.userDefinedName = false;
    }

    /**
     * @param name the variable name of the variable
     */
    VarImpl(String name) {
        this.name = name;
        this.userDefinedName = true;
    }

    /**
     * Create a variable by combining a collection of other variables
     * @param vars a collection of variables to combine
     */
    VarImpl(Collection vars) {
        VarAdmin first = vars.iterator().next();
        this.name = first.getName();
        this.userDefinedName = first.isUserDefinedName();

        for (VarAdmin var : vars) {
            if (var.isUserDefinedName()) {
                this.name = var.getName();
            }

            var.getProperties().forEach(this::addProperty);
        }
    }

    @Override
    public Var id(String id) {
        return addProperty(new IdProperty(id));
    }

    @Override
    public Var value() {
        return addProperty(new ValueFlagProperty());
    }

    @Override
    public Var value(Object value) {
        return value(eq(value));
    }

    @Override
    public Var value(ValuePredicate predicate) {
        return addProperty(new ValueProperty(predicate.admin()));
    }

    @Override
    public Var has(String type) {
        return has(type, var());
    }

    @Override
    public Var has(String type, Object value) {
        return has(type, eq(value));
    }

    @Override
    public Var has(String type, ValuePredicate predicate) {
        return has(type, var().value(predicate));
    }

    @Override
    public Var has(String type, Var var) {
        return addProperty(new HasResourceProperty(type, var.admin()));
    }

    @Override
    public Var isa(String type) {
        return isa(var().id(type));
    }

    @Override
    public Var isa(Var type) {
        return addProperty(new IsaProperty(type.admin()));
    }

    @Override
    public Var ako(String type) {
        return ako(var().id(type));
    }

    @Override
    public Var ako(Var type) {
        return addProperty(new AkoProperty(type.admin()));
    }

    @Override
    public Var hasRole(String type) {
        return hasRole(var().id(type));
    }

    @Override
    public Var hasRole(Var type) {
        return addProperty(new HasRoleProperty(type.admin()));
    }

    @Override
    public Var playsRole(String type) {
        return playsRole(var().id(type));
    }

    @Override
    public Var playsRole(Var type) {
        return addProperty(new PlaysRoleProperty(type.admin()));
    }

    @Override
    public Var hasScope(Var type) {
        return addProperty(new HasScopeProperty(type.admin()));
    }

    @Override
    public Var hasResource(String type) {
        return hasResource(var().id(type));
    }

    @Override
    public Var hasResource(Var type) {
        return addProperty(new HasResourceTypeProperty(type.admin()));
    }

    @Override
    public Var rel(String roleplayer) {
        return rel(var(roleplayer));
    }

    @Override
    public Var rel(Var roleplayer) {
        return addCasting(new Casting(roleplayer.admin()));
    }

    @Override
    public Var rel(String roletype, String roleplayer) {
        return rel(var().id(roletype), var(roleplayer));
    }

    @Override
    public Var rel(Var roletype, String roleplayer) {
        return rel(roletype, var(roleplayer));
    }

    @Override
    public Var rel(String roletype, Var roleplayer) {
        return rel(var().id(roletype), roleplayer);
    }

    @Override
    public Var rel(Var roletype, Var roleplayer) {
        return addCasting(new Casting(roletype.admin(), roleplayer.admin()));
    }

    @Override
    public Var isAbstract() {
        return addProperty(new IsAbstractProperty());
    }

    @Override
    public Var datatype(ResourceType.DataType datatype) {
        return addProperty(new DataTypeProperty(datatype));
    }

    @Override
    public Var regex(String regex) {
        return addProperty(new RegexProperty(regex));
    }

    @Override
    public Var lhs(String lhs) {
        return addProperty(new LhsProperty(lhs));
    }

    @Override
    public Var rhs(String rhs) {
        return addProperty(new RhsProperty(rhs));
    }

    @Override
    public VarAdmin admin() {
        return this;
    }

    @Override
    public Optional getType() {
        return getProperty(IsaProperty.class).map(IsaProperty::getType);
    }

    @Override
    public boolean isRelation() {
        return getProperty(RelationProperty.class).isPresent();
    }

    @Override
    public boolean isUserDefinedName() {
        return userDefinedName;
    }

    @Override
    public Optional getId() {
        return getProperty(IdProperty.class).map(IdProperty::getId);
    }

    @Override
    public boolean hasNoProperties() {
        // return true if this variable has any properties set
        return properties.isEmpty();
    }

    @Override
    public Optional getIdOnly() {

        if (getId().isPresent() && properties.size() == 1 && !userDefinedName) {
            return getId();
        } else {
            return Optional.empty();
        }
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setName(String name) {
        if (!userDefinedName) throw new RuntimeException(SET_GENERATED_VARIABLE_NAME.getMessage(name));
        this.name = name;
    }

    @Override
    public String getPrintableName() {
        if (userDefinedName) {
            return "$" + name;
        } else {
            return getId().map(StringConverter::idToString).orElse("'" + toString() + "'");
        }
    }

    @Override
    public Set getValueEqualsPredicates() {
        return getValuePredicates().stream()
                .map(ValuePredicateAdmin::equalsValue)
                .flatMap(CommonUtil::optionalToStream)
                .collect(toSet());
    }

    @Override
    public Set getValuePredicates() {
        return getProperties(ValueProperty.class).map(ValueProperty::getPredicate).collect(toSet());
    }

    @Override
    public Map> getResourcePredicates() {
        // Type of the resource is guaranteed to exist
        //noinspection OptionalGetWithoutIsPresent
        Function type = v -> v.getType().get();

        Function> predicates = resource -> resource.getValuePredicates().stream();

        Map> groupedByType =
                getProperties(HasResourceProperty.class).map(HasResourceProperty::getResource).collect(groupingBy(type));

        return Maps.transformValues(groupedByType, vars -> vars.stream().flatMap(predicates).collect(toSet()));
    }

    public Set getCastings() {
        return getProperties(RelationProperty.class).flatMap(RelationProperty::getCastings).collect(toSet());
    }

    @Override
    public Stream getProperties() {
        return properties.stream();
    }

    @Override
    public  Stream getProperties(Class type) {
        return getProperties().filter(type::isInstance).map(type::cast);
    }

    @Override
    public  Optional getProperty(Class type) {
        return getProperties().filter(type::isInstance).map(type::cast).findAny();
    }

    @Override
    public Set getInnerVars() {
        Stack newVars = new Stack<>();
        Set vars = new HashSet<>();

        newVars.add(this);

        while (!newVars.isEmpty()) {
            VarAdmin var = newVars.pop();
            vars.add(var);
            var.getProperties().flatMap(VarProperty::getInnerVars).forEach(newVars::add);
        }

        return vars;
    }

    @Override
    public Set getImplicitInnerVars() {
        Stack newVars = new Stack<>();
        Set vars = new HashSet<>();

        newVars.add(this);

        while (!newVars.isEmpty()) {
            VarAdmin var = newVars.pop();
            vars.add(var);
            var.getProperties().flatMap(VarProperty::getImplicitInnerVars).forEach(newVars::add);
        }

        return vars;
    }

    @Override
    public Set getTypeIds() {
        return getProperties()
                .flatMap(VarProperty::getTypes)
                .map(VarAdmin::getId).flatMap(CommonUtil::optionalToStream)
                .collect(toSet());
    }

    @Override
    public String toString() {
        Set innerVars = getInnerVars();
        innerVars.remove(this);
        getProperties(HasResourceProperty.class).map(HasResourceProperty::getResource).forEach(innerVars::remove);

        if (!innerVars.stream().allMatch(v -> v.getIdOnly().isPresent() || v.hasNoProperties())) {
            throw new UnsupportedOperationException("Graql strings cannot represent a query with inner variables");
        }

        StringBuilder builder = new StringBuilder();

        String name = isUserDefinedName() ? getPrintableName() + " " : "";

        builder.append(name);

        boolean first = true;

        for (VarProperty property : properties) {
            if (!first) {
                builder.append(" ");
            }
            first = false;
            property.buildString(builder);
        }

        return builder.toString();
    }

    private Var addCasting(VarAdmin.Casting casting) {
        Optional relationProperty = getProperty(RelationProperty.class);

        Stream oldCastings = relationProperty
                .map(RelationProperty::getCastings)
                .orElse(Stream.empty());

        ImmutableSet castings =
                Stream.concat(oldCastings, Stream.of(casting)).collect(toImmutableSet());

        relationProperty.ifPresent(properties::remove);

        properties.add(new RelationProperty(castings));

        return this;
    }

    /**
     * Add a non-unique property
     */
    private Var addProperty(VarProperty property) {
        if (property.isUnique()) {
            testUniqueProperty((UniqueVarProperty) property);
        }
        properties.add(property);
        return this;
    }

    /**
     * Fail if there is already an equal property of this type
     */
    private void testUniqueProperty(UniqueVarProperty property) {
        getProperty(property.getClass()).filter(other -> !other.equals(property)).ifPresent(other -> {
            String message = CONFLICTING_PROPERTIES.getMessage(
                    getPrintableName(), property.graqlString(), other.graqlString()
            );
            throw new IllegalStateException(message);
        });
    }

    @Override
    public Disjunction> getDisjunctiveNormalForm() {
        // a disjunction containing only one option
        Conjunction conjunction = Patterns.conjunction(Collections.singleton(this));
        return Patterns.disjunction(Collections.singleton(conjunction));
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        VarImpl var = (VarImpl) o;

        if (userDefinedName != var.userDefinedName) return false;
        if (!properties.equals(var.properties)) return false;
        return !userDefinedName || name.equals(var.name);

    }

    @Override
    public int hashCode() {
        int result = properties.hashCode();
        if (userDefinedName) result = 31 * result + name.hashCode();
        result = 31 * result + (userDefinedName ? 1 : 0);
        return result;
    }

    /**
     * A casting is the pairing of roletype and roleplayer in a relation, where the roletype may be unknown
     */
    public class Casting implements VarAdmin.Casting {
        private final Optional roleType;
        private final VarAdmin rolePlayer;

        /**
         * A casting without a role type specified
         * @param rolePlayer the role player of the casting
         */
        Casting(VarAdmin rolePlayer) {
            this.roleType = Optional.empty();
            this.rolePlayer = rolePlayer;
        }

        /**
         * @param roletype the role type of the casting
         * @param rolePlayer the role player of the casting
         */
        Casting(VarAdmin roletype, VarAdmin rolePlayer) {
            this.roleType = Optional.of(roletype);
            this.rolePlayer = rolePlayer;
        }

        @Override
        public Optional getRoleType() {
            return roleType;
        }

        @Override
        public VarAdmin getRolePlayer() {
            return rolePlayer;
        }

        @Override
        public String toString() {
            return getRoleType().map(r -> r.getPrintableName() + ": ").orElse("") + getRolePlayer().getPrintableName();
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            Casting casting = (Casting) o;

            if (!roleType.equals(casting.roleType)) return false;
            return rolePlayer.equals(casting.rolePlayer);

        }

        @Override
        public int hashCode() {
            int result = roleType.hashCode();
            result = 31 * result + rolePlayer.hashCode();
            return result;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy