org.commonjava.maven.atlas.graph.spi.neo4j.io.Conversions.java Source code

Java tutorial

Introduction

Here is the source code for org.commonjava.maven.atlas.graph.spi.neo4j.io.Conversions.java

Source

/**
 * Copyright (C) 2012 Red Hat, Inc. (jdcasey@commonjava.org)
 *
 * 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 org.commonjava.maven.atlas.graph.spi.neo4j.io;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
import org.commonjava.maven.atlas.graph.ViewParams;
import org.commonjava.maven.atlas.graph.jackson.ProjectRelationshipSerializerModule;
import org.commonjava.maven.atlas.graph.rel.DependencyRelationship;
import org.commonjava.maven.atlas.graph.rel.PluginDependencyRelationship;
import org.commonjava.maven.atlas.graph.rel.PluginRelationship;
import org.commonjava.maven.atlas.graph.rel.ProjectRelationship;
import org.commonjava.maven.atlas.graph.spi.neo4j.GraphAdmin;
import org.commonjava.maven.atlas.graph.spi.neo4j.GraphRelType;
import org.commonjava.maven.atlas.graph.spi.neo4j.NodeType;
import org.commonjava.maven.atlas.graph.spi.neo4j.model.AbstractNeoProjectRelationship;
import org.commonjava.maven.atlas.graph.spi.neo4j.model.CyclePath;
import org.commonjava.maven.atlas.graph.spi.neo4j.model.NeoArtifactRef;
import org.commonjava.maven.atlas.graph.spi.neo4j.model.NeoBomRelationship;
import org.commonjava.maven.atlas.graph.spi.neo4j.model.NeoDependencyRelationship;
import org.commonjava.maven.atlas.graph.spi.neo4j.model.NeoExtensionRelationship;
import org.commonjava.maven.atlas.graph.spi.neo4j.model.NeoParentRelationship;
import org.commonjava.maven.atlas.graph.spi.neo4j.model.NeoPluginDependencyRelationship;
import org.commonjava.maven.atlas.graph.spi.neo4j.model.NeoPluginRelationship;
import org.commonjava.maven.atlas.graph.spi.neo4j.model.NeoProjectVersionRef;
import org.commonjava.maven.atlas.graph.spi.neo4j.model.NeoTypeAndClassifier;
import org.commonjava.maven.atlas.ident.jackson.ProjectVersionRefSerializerModule;
import org.commonjava.maven.atlas.ident.ref.ArtifactRef;
import org.commonjava.maven.atlas.ident.ref.ProjectRef;
import org.commonjava.maven.atlas.ident.ref.ProjectVersionRef;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.Relationship;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static org.apache.commons.lang.StringUtils.join;

public final class Conversions {

    public static final String RELATIONSHIP_ID = "relationship_id";

    public static final String GROUP_ID = "groupId";

    public static final String ARTIFACT_ID = "artifactId";

    public static final String VERSION = "version";

    public static final String GAV = "gav";

    public static final String GA = "ga";

    public static final String INDEX = "index";

    public static final String IS_REPORTING_PLUGIN = "reporting";

    public static final String IS_MANAGED = "managed";

    public static final String IS_INHERITED = "inherited";

    public static final String IS_MIXIN = "mixin";

    public static final String PLUGIN_GROUP_ID = "plugin_groupId";

    public static final String PLUGIN_ARTIFACT_ID = "plugin_artifactId";

    public static final String TYPE = "type";

    public static final String CLASSIFIER = "classifier";

    public static final String SCOPE = "scope";

    public static final String OPTIONAL = "optional";

    public static final String EXCLUDES = "excludes";

    public static final String CYCLE_ID = "cycle_id";

    public static final String CYCLE_RELATIONSHIPS = "relationship_participants";

    public static final String CYCLE_PROJECTS = "project_participants";

    public static final String PROJECT_ERROR = "_error";

    private static final String METADATA_PREFIX = "_metadata_";

    public static final String NODE_TYPE = "_node_type";

    public static final String CYCLE_MEMBERSHIP = "cycle_membership";

    public static final String VARIABLE = "_variable";

    public static final String CONNECTED = "_connected";

    public static final String CYCLE_INJECTION = "_cycle_injection";

    public static final String CYCLES_INJECTED = "_cycles";

    public static final String SOURCE_URI = "source_uri";

    public static final String POM_LOCATION_URI = "pom_location_uri";

    public static final String LAST_ACCESS_DATE = "last_access";

    // graph-level configuration.

    public static final String LAST_ACCESS = "last_access";

    public static final String ACTIVE_POM_LOCATIONS = "active-pom-locations";

    public static final String ACTIVE_SOURCES = "active-pom-sources";

    public static final String CONFIG_PROPERTY_PREFIX = "_p_";

    public static final String VIEW_SHORT_ID = "view_sid";

    private static final String VIEW_DATA = "view_data";

    // cached path tracking...ONLY handled by Conversions, since the info is inlined.

    private static final String CYCLE_PATH_PREFIX = "cached_cycle_";

    // handled by other things, like updaters.

    public static final String RID = "rel_id";

    public static final String NID = "node_id";

    public static final String CONFIG_ID = "config_id";

    public static final String VIEW_ID = "view_id";

    public static final String CYCLE_DETECTION_PENDING = "cycle_detect_pending";

    private static final String MEMBERSHIP_DETECTION_PENDING = "member_detect_pending";

    private Conversions() {
    }

    public static int countArrayElements(final String property, final PropertyContainer container) {
        if (!container.hasProperty(property)) {
            return -1;
        }

        final Object value = container.getProperty(property);
        if (value.getClass().isArray()) {
            final Object[] elements = (Object[]) value;
            return elements.length;
        }

        return 1;
    }

    public static List<ProjectVersionRef> convertToDetachedProjects(final Iterable<Node> nodes) {
        final List<ProjectVersionRef> refs = new ArrayList<ProjectVersionRef>();
        for (final Node node : nodes) {
            if (node.getId() == 0) {
                continue;
            }

            if (!Conversions.isType(node, NodeType.PROJECT)) {
                continue;
            }

            refs.add(Conversions.toProjectVersionRef(node).detach());
        }

        return refs;
    }

    public static List<NeoProjectVersionRef> convertToProjects(final Iterable<Node> nodes) {
        final List<NeoProjectVersionRef> refs = new ArrayList<NeoProjectVersionRef>();
        for (final Node node : nodes) {
            if (node.getId() == 0) {
                continue;
            }

            if (!Conversions.isType(node, NodeType.PROJECT)) {
                continue;
            }

            refs.add(Conversions.toProjectVersionRef(node));
        }

        return refs;
    }

    /**
     * Converts an iterable of relationships to project relationships detached from database.
     *
     * @param relationships iterable of {@link AbstractNeoProjectRelationship} or {@link Relationship}
     * @return list of detached relationships
     * @throws IllegalArgumentException if an iterable of unsupported classes is passed
     */
    public static List<ProjectRelationship<?, ?>> convertToDetachedRelationships(final Iterable<?> relationships) {
        final List<ProjectRelationship<?, ?>> rels = new ArrayList<ProjectRelationship<?, ?>>();
        Iterator<?> iterator = relationships.iterator();
        while (iterator.hasNext()) {
            final AbstractNeoProjectRelationship<?, ?, ?> rel;
            Object next = iterator.next();
            if (next instanceof AbstractNeoProjectRelationship) {
                rel = (AbstractNeoProjectRelationship<?, ?, ?>) next;
            } else if (next instanceof Relationship) {
                rel = Conversions.toProjectRelationship((Relationship) next);
            } else {
                throw new IllegalArgumentException("Relationship class " + next.getClass().getCanonicalName()
                        + " cannot be converted to detached relationship.");
            }
            if (rel != null) {
                rels.add(rel.detach());
            }
        }

        return rels;
    }

    public static List<AbstractNeoProjectRelationship<?, ?, ?>> convertToRelationships(
            final Iterable<Relationship> relationships) {
        final List<AbstractNeoProjectRelationship<?, ?, ?>> rels = new ArrayList<AbstractNeoProjectRelationship<?, ?, ?>>();
        for (final Relationship relationship : relationships) {
            final AbstractNeoProjectRelationship<?, ?, ?> rel = Conversions.toProjectRelationship(relationship);
            if (rel != null) {
                rels.add(rel);
            }
        }

        return rels;
    }

    public static List<ProjectRelationship<?, ?>> convertToDetachedRelationships(final Iterable<Long> relationships,
            final GraphAdmin admin) {
        final List<ProjectRelationship<?, ?>> rels = new ArrayList<ProjectRelationship<?, ?>>();
        for (final Long rid : relationships) {
            final Relationship relationship = admin.getRelationship(rid);
            final ProjectRelationship<?, ?> rel = toProjectRelationship(relationship).detach();
            if (rel != null) {
                rels.add(rel);
            }
        }

        return rels;
    }

    public static List<AbstractNeoProjectRelationship<?, ?, ?>> convertToRelationships(
            final Iterable<Long> relationships, final GraphAdmin admin) {
        final List<AbstractNeoProjectRelationship<?, ?, ?>> rels = new ArrayList<AbstractNeoProjectRelationship<?, ?, ?>>();
        for (final Long rid : relationships) {
            final Relationship relationship = admin.getRelationship(rid);
            final AbstractNeoProjectRelationship<?, ?, ?> rel = toProjectRelationship(relationship);
            if (rel != null) {
                rels.add(rel);
            }
        }

        return rels;
    }

    public static void toNodeProperties(final ProjectVersionRef ref, final Node node, final boolean connected) {
        Logger logger = LoggerFactory.getLogger(Conversions.class);

        logger.debug("Adding {} (type: {}) to node: {}", ref, ref.getClass().getSimpleName(), node);
        if (!(ref instanceof NeoProjectVersionRef) || ((NeoProjectVersionRef) ref).isDirty()) {
            final String g = ref.getGroupId();
            final String a = ref.getArtifactId();
            final String v = ref.getVersionString();

            if (empty(g) || empty(a) || empty(v)) {
                throw new IllegalArgumentException(String.format("GAV cannot contain nulls: %s:%s:%s", g, a, v));
            }

            node.setProperty(ARTIFACT_ID, a);
            node.setProperty(GROUP_ID, g);

            logger.debug("Setting property: {} with value: {} for node: {}", VERSION, v, node.getId());
            node.setProperty(VERSION, v);

            node.setProperty(GAV, ref.toString());
        }

        node.setProperty(NODE_TYPE, NodeType.PROJECT.name());

        if (ref.isVariableVersion()) {
            node.setProperty(VARIABLE, true);
        }

        markConnected(node, connected);
        //
        //        logger.debug( "groupId of {} is:\nNeoIdentityUtils: {}\nConversions: {}\nDirect access: {}", node,
        //                      NeoIdentityUtils.getStringProperty( node, GROUP_ID, null, null ),
        //                      getStringProperty( GROUP_ID, node ), node.hasProperty( GROUP_ID ) ? node.getProperty( GROUP_ID ) : null );
    }

    public static boolean isAtlasType(final Relationship rel) {
        return GraphRelType.valueOf(rel.getType().name()).isAtlasRelationship();
    }

    public static boolean isType(final Node node, final NodeType type) {
        final String nt = getStringProperty(NODE_TYPE, node);
        return nt != null && type == NodeType.valueOf(nt);
    }

    //    public static ProjectVersionRef toProjectVersionRef( final Node node )
    //    {
    //        return toProjectVersionRef( node, null );
    //    }

    public static NeoProjectVersionRef toProjectVersionRef(final Node node) {
        if (node == null) {
            return null;
        }

        if (!isType(node, NodeType.PROJECT)) {
            throw new IllegalArgumentException("Node " + node.getId() + " is not a project reference.");
        }

        final NeoProjectVersionRef result = new NeoProjectVersionRef(node);
        return result;
    }

    private static boolean empty(final String val) {
        return val == null || val.trim().length() < 1;
    }

    @SuppressWarnings("incomplete-switch")
    public static void toRelationshipProperties(final ProjectRelationship<?, ?> rel,
            final Relationship relationship) {
        relationship.setProperty(INDEX, rel.getIndex());
        String[] srcs = toStringArray(rel.getSources());
        Logger logger = LoggerFactory.getLogger(Conversions.class);
        logger.debug("Storing rel: {}\nwith sources: {}\n in property: {}\nRelationship: {}", rel,
                Arrays.toString(srcs), SOURCE_URI, relationship);
        relationship.setProperty(SOURCE_URI, srcs);
        relationship.setProperty(POM_LOCATION_URI, rel.getPomLocation().toString());
        relationship.setProperty(IS_INHERITED, rel.isInherited());

        switch (rel.getType()) {
        case BOM:
            relationship.setProperty(IS_MIXIN, rel.isMixin());
            break;
        case DEPENDENCY: {
            final DependencyRelationship specificRel = (DependencyRelationship) rel;
            toRelationshipProperties((ArtifactRef) rel.getTarget(), relationship);
            relationship.setProperty(IS_MANAGED, specificRel.isManaged());
            relationship.setProperty(SCOPE, specificRel.getScope().realName());
            relationship.setProperty(OPTIONAL, specificRel.isOptional());

            final Set<ProjectRef> excludes = specificRel.getExcludes();
            if (excludes != null && !excludes.isEmpty()) {
                final StringBuilder sb = new StringBuilder();
                for (final ProjectRef exclude : excludes) {
                    if (sb.length() > 0) {
                        sb.append(",");
                    }

                    sb.append(exclude.getGroupId()).append(":").append(exclude.getArtifactId());
                }

                relationship.setProperty(EXCLUDES, sb.toString());
            }

            break;
        }
        case PLUGIN_DEP: {
            toRelationshipProperties((ArtifactRef) rel.getTarget(), relationship);

            final PluginDependencyRelationship specificRel = (PluginDependencyRelationship) rel;

            final ProjectRef plugin = specificRel.getPlugin();
            relationship.setProperty(PLUGIN_ARTIFACT_ID, plugin.getArtifactId());
            relationship.setProperty(PLUGIN_GROUP_ID, plugin.getGroupId());
            relationship.setProperty(IS_MANAGED, specificRel.isManaged());

            break;
        }
        case PLUGIN: {
            final PluginRelationship specificRel = (PluginRelationship) rel;
            relationship.setProperty(IS_MANAGED, specificRel.isManaged());
            relationship.setProperty(IS_REPORTING_PLUGIN, specificRel.isReporting());

            break;
        }
        }
    }

    public static String[] toStringArray(final Collection<?> sources) {
        final Set<String> result = new LinkedHashSet<String>(sources.size());
        for (final Object object : sources) {
            if (object == null) {
                continue;
            }

            result.add(object.toString());
        }

        return result.toArray(new String[result.size()]);
    }

    //    public static ProjectRelationship<?> toProjectRelationship( final Relationship rel )
    //    {
    //        return toProjectRelationship( rel, null );
    //    }

    public static AbstractNeoProjectRelationship<?, ?, ?> toProjectRelationship(final Relationship rel) {
        if (rel == null) {
            return null;
        }

        final GraphRelType mapper = GraphRelType.valueOf(rel.getType().name());

        //        LOGGER.debug( "Converting relationship of type: {} (atlas type: {})", mapper,
        //                                              mapper.atlasType() );

        if (!mapper.isAtlasRelationship()) {
            return null;
        }

        if (rel.getStartNode() == null || rel.getEndNode() == null || !isType(rel.getStartNode(), NodeType.PROJECT)
                || !isType(rel.getEndNode(), NodeType.PROJECT)) {
            return null;
        }

        AbstractNeoProjectRelationship<?, ?, ?> result = null;
        switch (mapper.atlasType()) {
        case DEPENDENCY: {
            result = new NeoDependencyRelationship(rel);
            break;
        }
        case PLUGIN_DEP: {
            result = new NeoPluginDependencyRelationship(rel);
            break;
        }
        case PLUGIN: {
            result = new NeoPluginRelationship(rel);
            break;
        }
        case EXTENSION: {
            result = new NeoExtensionRelationship(rel);
            break;
        }
        case BOM: {
            result = new NeoBomRelationship(rel);
            break;
        }
        case PARENT: {
            result = new NeoParentRelationship(rel);
            break;
        }
        default: {
            throw new IllegalArgumentException(
                    "I don't know how to construct the atlas relationship for type: " + mapper.atlasType());
        }
        }

        //        LOGGER.debug( "Returning project relationship: {}", result );
        return result;
    }

    public static String id(final ProjectRelationship<?, ?> rel) {
        try {
            String json = newMapper().writeValueAsString(rel);

            // FIXME: Rookie mistake! You can't add debug info to toString() with this here.
            return DigestUtils.shaHex(json);
        } catch (JsonProcessingException e) {
            throw new IllegalArgumentException("Cannot serialize relationship for ID generation: " + e.getMessage(),
                    e);
        }
    }

    private static ObjectMapper newMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModules(ProjectVersionRefSerializerModule.INSTANCE,
                ProjectRelationshipSerializerModule.INSTANCE,
                NeoSpecificProjectRelationshipSerializerModule.INSTANCE,
                NeoSpecificProjectVersionRefSerializerModule.INSTANCE);

        mapper.disable(SerializationFeature.WRITE_NULL_MAP_VALUES, SerializationFeature.WRITE_EMPTY_JSON_ARRAYS);
        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

        return mapper;
    }

    private static ArtifactRef toArtifactRef(final ProjectVersionRef ref, final Relationship rel) {
        if (ref == null) {
            return null;
        }

        return new NeoArtifactRef(ref, new NeoTypeAndClassifier(rel));
    }

    private static void toRelationshipProperties(final ArtifactRef target, final Relationship relationship) {
        Logger logger = LoggerFactory.getLogger(Conversions.class);
        logger.debug("Type of artifact: {} (type: {}) is: {}", target, target.getClass().getSimpleName(),
                target.getType());
        relationship.setProperty(TYPE, target.getType());

        if (target.getClassifier() != null) {
            relationship.setProperty(CLASSIFIER, target.getClassifier());
        }
    }

    public static String getStringProperty(final String prop, final PropertyContainer container) {
        if (container.hasProperty(prop)) {
            return (String) container.getProperty(prop);
        }
        return null;
    }

    public static Set<URI> getURISetProperty(final String prop, final PropertyContainer container,
            final URI defaultValue) {
        final Set<URI> result = new HashSet<URI>();

        if (container.hasProperty(prop)) {
            final String[] uris = (String[]) container.getProperty(prop);
            for (final String uri : uris) {
                try {
                    final URI u = new URI(uri);
                    if (!result.contains(u)) {
                        result.add(u);
                    }
                } catch (final URISyntaxException e) {
                    Logger logger = LoggerFactory.getLogger(Conversions.class);
                    logger.warn(String.format("Failed to construct URI from: %s\nContainer: %s\nReason: %s",
                            container, uri, e.getMessage()), e);
                }
            }
        }

        if (defaultValue != null && result.isEmpty()) {
            result.add(defaultValue);
        }

        return result;
    }

    public static void addToURISetProperty(final Collection<URI> uris, final String prop,
            final PropertyContainer container) {
        if (uris == null || uris.isEmpty()) {
            return;
        }

        final Set<URI> existing = getURISetProperty(prop, container, null);
        for (final URI uri : uris) {
            existing.add(uri);
        }

        container.setProperty(prop, toStringArray(existing));
    }

    public static void removeFromURISetProperty(final Collection<URI> uris, final String prop,
            final PropertyContainer container) {
        if (uris == null || uris.isEmpty() || !container.hasProperty(prop)) {
            return;
        }

        final Set<URI> existing = getURISetProperty(prop, container, null);
        for (final URI uri : uris) {
            existing.remove(uri);
        }

        if (existing.isEmpty()) {
            container.removeProperty(prop);
        } else {
            container.setProperty(prop, toStringArray(existing));
        }
    }

    public static URI getURIProperty(final String prop, final PropertyContainer container, final URI defaultValue) {
        URI result = defaultValue;

        if (container.hasProperty(prop)) {
            try {
                result = new URI((String) container.getProperty(prop));
            } catch (final URISyntaxException e) {
            }
        }

        return result;
    }

    public static Boolean getBooleanProperty(final String prop, final PropertyContainer container) {
        if (container.hasProperty(prop)) {
            return (Boolean) container.getProperty(prop);
        }
        return null;
    }

    public static Boolean getBooleanProperty(final String prop, final PropertyContainer container,
            final Boolean defaultValue) {
        if (container.hasProperty(prop)) {
            return (Boolean) container.getProperty(prop);
        }

        return defaultValue;
    }

    public static Integer getIntegerProperty(final String prop, final PropertyContainer container) {
        if (container.hasProperty(prop)) {
            return (Integer) container.getProperty(prop);
        }
        return null;
    }

    public static Integer getIntegerProperty(final String prop, final PropertyContainer container,
            final int defaultValue) {
        if (container.hasProperty(prop)) {
            return (Integer) container.getProperty(prop);
        }

        return defaultValue;
    }

    public static Long getLongProperty(final String prop, final PropertyContainer container,
            final long defaultValue) {
        if (container.hasProperty(prop)) {
            return (Long) container.getProperty(prop);
        }

        return defaultValue;
    }

    public static String setConfigProperty(final String key, final String value,
            final PropertyContainer container) {
        final String pkey = CONFIG_PROPERTY_PREFIX + key;
        final String old = container.hasProperty(pkey) ? (String) container.getProperty(pkey) : null;

        container.setProperty(pkey, value);

        return old;
    }

    public static String removeConfigProperty(final String key, final PropertyContainer container) {
        final String pkey = CONFIG_PROPERTY_PREFIX + key;
        String old = null;
        if (container.hasProperty(pkey)) {
            old = (String) container.getProperty(pkey);

            container.removeProperty(pkey);
        }

        return old;
    }

    public static String getConfigProperty(final String key, final PropertyContainer container,
            final String defaultValue) {
        final String result = getStringProperty(CONFIG_PROPERTY_PREFIX + key, container);

        return result == null ? defaultValue : result;
    }

    public static void setMetadata(final String key, final String value, final PropertyContainer container) {
        container.setProperty(METADATA_PREFIX + key, value);
    }

    public static void setMetadata(final Map<String, String> metadata, final PropertyContainer container) {
        final Map<String, String> metadataMap = getMetadataMap(container);

        if (metadataMap != null) {
            for (final String key : metadataMap.keySet()) {
                container.removeProperty(key);
            }
        }

        for (final Map.Entry<String, String> entry : metadata.entrySet()) {
            container.setProperty(METADATA_PREFIX + entry.getKey(), entry.getValue());
        }
    }

    public static Map<String, String> getMetadataMap(final PropertyContainer container) {
        return getMetadataMap(container, null);
    }

    public static Map<String, String> getMetadataMap(final PropertyContainer container,
            final Set<String> matching) {
        final Iterable<String> keys = container.getPropertyKeys();
        final Map<String, String> md = new HashMap<String, String>();
        for (final String key : keys) {
            if (!key.startsWith(METADATA_PREFIX)) {
                continue;
            }

            final String k = key.substring(METADATA_PREFIX.length());
            if (matching != null && !matching.contains(k)) {
                continue;
            }

            final String value = getStringProperty(key, container);

            md.put(k, value);
        }

        return md.isEmpty() ? null : md;
    }

    public static String getMetadata(final String key, final PropertyContainer container) {
        return getStringProperty(METADATA_PREFIX + key, container);
    }

    public static void toNodeProperties(final String cycleId, final String rawCycleId,
            final Set<ProjectVersionRef> refs, final Node node) {
        node.setProperty(NODE_TYPE, NodeType.CYCLE.name());
        node.setProperty(CYCLE_ID, cycleId);
        node.setProperty(CYCLE_RELATIONSHIPS, rawCycleId);
        node.setProperty(CYCLE_PROJECTS, join(refs, ","));
    }

    public static boolean isConnected(final Node node) {
        return getBooleanProperty(CONNECTED, node);
    }

    public static void markConnected(final Node node, final boolean connected) {
        //        LOGGER.info( "Marking as connected (non-missing): {}", node.getProperty( GAV ) );
        node.setProperty(CONNECTED, connected);
    }

    public static void markCycleInjection(final Relationship relationship, final Set<List<Relationship>> cycles) {
        relationship.setProperty(CYCLE_INJECTION, true);
        final List<Long> collapsed = new ArrayList<Long>();
        final Set<List<Long>> existing = getInjectedCycles(relationship);
        if (existing != null && !existing.isEmpty()) {
            for (final List<Long> cycle : existing) {
                if (!collapsed.isEmpty()) {
                    collapsed.add(-1L);
                }

                collapsed.addAll(cycle);
            }
        }

        for (final List<Relationship> cycle : cycles) {
            if (existing.contains(cycle)) {
                continue;
            }

            if (!collapsed.isEmpty()) {
                collapsed.add(-1L);
            }

            boolean containsGivenRelationship = false;
            for (final Relationship r : cycle) {
                final long rid = r.getId();

                collapsed.add(rid);
                if (rid == relationship.getId()) {
                    containsGivenRelationship = true;
                }
            }

            if (!containsGivenRelationship) {
                collapsed.add(relationship.getId());
            }
        }

        final long[] arry = new long[collapsed.size()];
        int i = 0;
        for (final Long l : collapsed) {
            arry[i] = l;
            i++;
        }

        relationship.setProperty(CYCLES_INJECTED, arry);
    }

    public static Set<List<Long>> getInjectedCycles(final Relationship relationship) {
        final Set<List<Long>> cycles = new HashSet<List<Long>>();

        if (relationship.hasProperty(CYCLES_INJECTED)) {
            final long[] collapsed = (long[]) relationship.getProperty(CYCLES_INJECTED);

            List<Long> currentCycle = new ArrayList<Long>();
            for (final long id : collapsed) {
                if (id == -1) {
                    if (!currentCycle.isEmpty()) {
                        cycles.add(currentCycle);
                        currentCycle = new ArrayList<Long>();
                    }
                } else {
                    currentCycle.add(id);
                }
            }

            if (!currentCycle.isEmpty()) {
                cycles.add(currentCycle);
            }
        }

        return cycles;
    }

    public static void removeProperty(final String key, final PropertyContainer container) {
        if (container.hasProperty(key)) {
            container.removeProperty(key);
        }
    }

    public static <T, P> Set<P> toProjectedSet(final Iterable<T> src, final Projector<T, P> projector) {
        final Set<P> set = new HashSet<P>();
        for (final T t : src) {
            set.add(projector.project(t));
        }

        return set;
    }

    public static <T> Set<T> toSet(final Iterable<T> src) {
        final Set<T> set = new HashSet<T>();
        for (final T t : src) {
            set.add(t);
        }

        return set;
    }

    public static <T> List<T> toList(final Iterable<T> src) {
        final List<T> set = new ArrayList<T>();
        for (final T t : src) {
            set.add(t);
        }

        return set;
    }

    public static void cloneRelationshipProperties(final Relationship from, final Relationship to) {
        final Iterable<String> keys = from.getPropertyKeys();
        for (final String key : keys) {
            to.setProperty(key, from.getProperty(key));
        }
    }

    public static void storeCachedCyclePath(final CyclePath path, final Node viewNode) {
        viewNode.setProperty(CYCLE_PATH_PREFIX + path.getKey(), path.getRelationshipIds());
    }

    public static Set<CyclePath> getCachedCyclePaths(final Node viewNode) {
        final Set<CyclePath> cycles = new HashSet<CyclePath>();
        for (final String key : viewNode.getPropertyKeys()) {
            if (key.startsWith(CYCLE_PATH_PREFIX)) {
                final long[] ids = (long[]) viewNode.getProperty(key);
                cycles.add(new CyclePath(ids));
            }
        }

        return cycles;
    }

    public static void storeView(final ViewParams params, final Node viewNode) {
        viewNode.setProperty(Conversions.VIEW_SHORT_ID, params.getShortId());

        ObjectOutputStream oos = null;
        try {
            final ByteArrayOutputStream baos = new ByteArrayOutputStream();

            oos = new ObjectOutputStream(baos);
            oos.writeObject(params);

            viewNode.setProperty(VIEW_DATA, baos.toByteArray());
        } catch (final IOException e) {
            throw new IllegalStateException("Cannot construct ObjectOutputStream to wrap ByteArrayOutputStream!",
                    e);
        } finally {
            IOUtils.closeQuietly(oos);
        }
    }

    //    public static GraphView retrieveView( final Node viewNode, final AbstractNeo4JEGraphDriver driver )
    //    {
    //        return retrieveView( viewNode, null, driver );
    //    }

    public static ViewParams retrieveView(final Node viewNode, final GraphAdmin maint) {
        if (!viewNode.hasProperty(VIEW_DATA)) {
            return null;
        }

        final byte[] data = (byte[]) viewNode.getProperty(VIEW_DATA);

        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new ByteArrayInputStream(data));
            final ViewParams view = (ViewParams) ois.readObject();

            return view;
        } catch (final IOException e) {
            throw new IllegalStateException(
                    "Cannot construct ObjectInputStream to wrap ByteArrayInputStream containing " + data.length
                            + " bytes!",
                    e);
        } catch (final ClassNotFoundException e) {
            throw new IllegalStateException("Cannot read ViewParams. A class was missing: " + e.getMessage(), e);
        } finally {
            IOUtils.closeQuietly(ois);
        }
    }

    public static boolean isCycleDetectionPending(final Node viewNode) {
        return getBooleanProperty(CYCLE_DETECTION_PENDING, viewNode, Boolean.FALSE);
    }

    public static void setCycleDetectionPending(final Node viewNode, final boolean pending) {
        viewNode.setProperty(CYCLE_DETECTION_PENDING, pending);
    }

    public static boolean isMembershipDetectionPending(final Node viewNode) {
        return getBooleanProperty(MEMBERSHIP_DETECTION_PENDING, viewNode, Boolean.FALSE);
    }

    public static void setMembershipDetectionPending(final Node viewNode, final boolean pending) {
        viewNode.setProperty(MEMBERSHIP_DETECTION_PENDING, pending);
    }

    private static final String SELECTION_ORIGIN_PREFIX = "_selection_origin_";

    private static final String DESELECTION_TARGET_PREFIX = "_deselection_target_";

    public static final String ATLAS_RELATIONSHIP_COUNT = "_atlas_relationship_count";

    public static final String ATLAS_RELATIONSHIP_INDEX = "_atlas_relationship_index";

    public static long getDeselectionTarget(final long originRid, final Node viewNode) {
        return getLongProperty(DESELECTION_TARGET_PREFIX + originRid, viewNode, -1);
    }

    public static long getSelectionOrigin(final long targetRid, final Node viewNode) {
        return getLongProperty(SELECTION_ORIGIN_PREFIX + targetRid, viewNode, -1);
    }

    public static void setSelection(final long originRid, final long targetRid, final Node viewNode) {
        Logger logger = LoggerFactory.getLogger(Conversions.class);
        logger.info("Setting selection. Deselecting: {} in favor of: {} (view-node: {})", originRid, targetRid,
                viewNode);
        viewNode.setProperty(DESELECTION_TARGET_PREFIX + originRid, targetRid);
        viewNode.setProperty(SELECTION_ORIGIN_PREFIX + targetRid, originRid);
    }

    public static void removeSelectionByTarget(final long targetRid, final Node viewNode) {
        final String selKey = SELECTION_ORIGIN_PREFIX + targetRid;
        final long originRid = getLongProperty(selKey, viewNode, -1);

        if (originRid > -1) {
            viewNode.removeProperty(selKey);
        } else {
            return;
        }

        final String deKey = DESELECTION_TARGET_PREFIX + originRid;
        if (viewNode.hasProperty(deKey)) {
            viewNode.removeProperty(deKey);
        }
    }

    public static void removeSelectionByOrigin(final long originRid, final Node viewNode) {
        final String deKey = DESELECTION_TARGET_PREFIX + originRid;
        final long targetRid = getLongProperty(deKey, viewNode, -1);
        if (targetRid > -1) {
            viewNode.removeProperty(deKey);
        } else {
            return;
        }

        final String selKey = SELECTION_ORIGIN_PREFIX + targetRid;
        if (viewNode.hasProperty(selKey)) {
            viewNode.removeProperty(selKey);
        }
    }

    public static void storeError(final Node node, final String error) {
        node.setProperty(PROJECT_ERROR, error);
    }

    public static String getError(final Node node) {
        if (!node.hasProperty(PROJECT_ERROR)) {
            return null;
        }

        return (String) node.getProperty(PROJECT_ERROR);
    }

}