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

io.mindmaps.graph.internal.RelationImpl 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.graph.internal;

import io.mindmaps.util.Schema;
import io.mindmaps.util.ErrorMessage;
import io.mindmaps.exception.ConceptException;
import io.mindmaps.concept.Instance;
import io.mindmaps.concept.Relation;
import io.mindmaps.concept.RelationType;
import io.mindmaps.concept.RoleType;
import org.apache.tinkerpop.gremlin.structure.Direction;
import org.apache.tinkerpop.gremlin.structure.Vertex;

import java.util.*;

/**
 * A relation represents and instance of a relation type which concept how different entities relate to one another.
 */
class RelationImpl extends InstanceImpl implements Relation {
    RelationImpl(Vertex v, RelationType type, AbstractMindmapsGraph mindmapsGraph) {
        super(v, type, mindmapsGraph);
    }

    /**
     *
     * @return All the castings this relation is connected with
     */
    public Set getMappingCasting() {
        Set castings = new HashSet<>();
        getOutgoingNeighbours(Schema.EdgeLabel.CASTING).forEach(casting -> castings.add(casting.asCasting()));
        return castings;
    }

    /**
     * Sets the internal hash in order to perform a faster lookup
     * @param roleMap The roles and their corresponding role players
     */
    public void setHash(Map roleMap){
        if(roleMap == null || roleMap.isEmpty())
            setUniqueProperty(Schema.ConceptProperty.INDEX, "RelationBaseId_" + getBaseIdentifier() + UUID.randomUUID().toString());
        else
            setUniqueProperty(Schema.ConceptProperty.INDEX, generateNewHash(type(), roleMap));
    }

    /**
     *
     * @param relationType The type of this relation
     * @param roleMap The roles and their corresponding role players
     * @return A unique hash identifying this relation
     */
    public static String generateNewHash(RelationType relationType, Map roleMap){
        SortedSet sortedRoleIds = new TreeSet<>(roleMap.keySet());
        String hash = "RelationType_" + relationType.getId().replace("_", "\\_") + "_Relation";

        for(RoleType role: sortedRoleIds){
            hash = hash + "_" + role.getId().replace("_", "\\_") ;
            Instance instance = roleMap.get(role);
            if(instance != null){
                hash = hash + "_" + instance.getId().replace("_", "\\_") ;
            }
        }
        return hash;
    }

    /**
     *
     * @return A list of all the Instances involved in the relationships and the Role Types which they play.
     */
    @Override
    public Map rolePlayers() {
        Set castings = getMappingCasting();
        HashMap roleMap = new HashMap<>();

        //Gets roles based on all roles of the relation type
        type().hasRoles().forEach(roleType -> roleMap.put(roleType, null));

        //Get roles based on availiable castings
        castings.forEach(casting -> roleMap.put(casting.getRole(), casting.getRolePlayer()));

        return roleMap;
    }

    /**
     *
     * @return A list of the Instances which scope this Relation
     */
    @Override
    public Set scopes() {
        HashSet scopes = new HashSet<>();
        getOutgoingNeighbours(Schema.EdgeLabel.HAS_SCOPE).forEach(concept -> scopes.add(concept.asInstance()));
        return scopes;
    }

    /**
     *
     * @param instance A new instance which can scope this Relation
     * @return The Relation itself
     */
    @Override
    public Relation scope(Instance instance) {
        putEdge(instance, Schema.EdgeLabel.HAS_SCOPE);
        return this;
    }

    /**
     * Expands this Relation to include a new role player which is playing a specific role.
     * @param roleType The role of the new role player.
     * @param instance The new role player.
     * @return The Relation itself
     */
    @Override
    public Relation putRolePlayer(RoleType roleType, Instance instance) {
        if(roleType == null){
            throw new IllegalArgumentException(ErrorMessage.ROLE_IS_NULL.getMessage(instance));
        }

        if(mindmapsGraph.isBatchLoadingEnabled()) {
            return addNewRolePlayer(null, roleType, instance);
        } else {
            Map roleMap = rolePlayers();
            roleMap.put(roleType, instance);
            Relation otherRelation = mindmapsGraph.getRelation(type(), roleMap);

            if(otherRelation == null){
                return addNewRolePlayer(roleMap, roleType, instance);
            }

            if(!this.equals(otherRelation)){
                throw new ConceptException(ErrorMessage.RELATION_EXISTS.getMessage(otherRelation));
            } else {
                return this;
            }
        }
    }

    /**
     * Adds a new role player to this relation
     * @param roleType The role of the new role player.
     * @param instance The new role player.
     * @return The Relation itself
     */
    private Relation addNewRolePlayer(Map roleMap, RoleType roleType, Instance instance){
        if(instance != null)
            mindmapsGraph.putCasting((RoleTypeImpl) roleType, (InstanceImpl) instance, this);

        if(mindmapsGraph.isBatchLoadingEnabled()){
            setHash(null);
        } else {
            setHash(roleMap);
        }

        return this;
    }

    /**
     * @param scope A concept which is currently scoping this concept.
     * @return The Relation itself
     */
    @Override
    public Relation deleteScope(Instance scope) throws ConceptException {
        deleteEdgeTo(Schema.EdgeLabel.HAS_SCOPE, scope);
        return this;
    }

    /**
     * When a relation is deleted this cleans up any solitary casting and resources.
     */
    public void cleanUp() {
        boolean performDeletion = true;
        Collection rolePlayers = rolePlayers().values();

        // tracking
        rolePlayers.forEach(r -> {
            if(r != null)
                getMindmapsGraph().getConceptLog().putConcept((ConceptImpl) r);
        });
        this.getMappingCasting().forEach(c -> getMindmapsGraph().getConceptLog().putConcept(c));

        for(Instance instance : rolePlayers){
            if(instance != null && (instance.getId() != null )){
                performDeletion = false;
            }
        }

        if(performDeletion){
            delete();
        }
    }

    /**
     * Deletes the concept as a Relation
     */
    @Override
    public void innerDelete() {
        scopes().forEach(this::deleteScope);
        Set castings = getMappingCasting();

        for (CastingImpl casting: castings) {
            InstanceImpl instance = casting.getRolePlayer();
            if(instance != null) {
                for (EdgeImpl edge : instance.getEdgesOfType(Direction.BOTH, Schema.EdgeLabel.SHORTCUT)) {
                    if(edge.getProperty(Schema.EdgeProperty.RELATION_ID).equals(getId())){
                        edge.delete();
                    }
                }
            }
        }

        super.innerDelete();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy