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

Java tutorial

Introduction

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

Source

/*******************************************************************************
 * Copyright (C) 2013 John Casey.
 * 
 * This program 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.
 * 
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package org.commonjava.maven.atlas.spi.neo4j.io;

import static org.apache.commons.lang.StringUtils.join;
import static org.apache.maven.graph.effective.util.RelationshipUtils.POM_ROOT_URI;
import static org.apache.maven.graph.effective.util.RelationshipUtils.UNKNOWN_SOURCE_URI;

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.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.maven.graph.common.DependencyScope;
import org.apache.maven.graph.common.ref.ArtifactRef;
import org.apache.maven.graph.common.ref.ProjectRef;
import org.apache.maven.graph.common.ref.ProjectVersionRef;
import org.apache.maven.graph.effective.rel.DependencyRelationship;
import org.apache.maven.graph.effective.rel.ExtensionRelationship;
import org.apache.maven.graph.effective.rel.ParentRelationship;
import org.apache.maven.graph.effective.rel.PluginDependencyRelationship;
import org.apache.maven.graph.effective.rel.PluginRelationship;
import org.apache.maven.graph.effective.rel.ProjectRelationship;
import org.commonjava.maven.atlas.spi.neo4j.effective.GraphRelType;
import org.commonjava.maven.atlas.spi.neo4j.effective.NodeType;
import org.commonjava.util.logging.Logger;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Path;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.Relationship;

public final class Conversions {

    private static final Logger LOGGER = new Logger(Conversions.class);

    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 INDEX = "index";

    public static final String IS_REPORTING_PLUGIN = "reporting";

    public static final String IS_MANAGED = "managed";

    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";

    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 SELECTED_FOR = "_selected_for";

    public static final String DESELECTED_FOR = "_deselected_for";

    public static final String CLONE_OF = "_clone_of";

    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";

    private Conversions() {
    }

    public static List<ProjectVersionRef> convertToProjects(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));
        }

        return refs;
    }

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

        return rels;
    }

    public static void toNodeProperties(final ProjectVersionRef ref, final Node node, final boolean connected) {
        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(NODE_TYPE, NodeType.PROJECT.name());
        node.setProperty(ARTIFACT_ID, a);
        node.setProperty(GROUP_ID, g);
        node.setProperty(VERSION, v);
        node.setProperty(GAV, ref.toString());

        if (ref.isVariableVersion()) {
            LOGGER.debug("Marking: %s as variable.", ref);
            node.setProperty(VARIABLE, true);
        }

        markConnected(node, connected);
    }

    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) {
        if (node == null) {
            return null;
        }

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

        final String g = getStringProperty(GROUP_ID, node);
        final String a = getStringProperty(ARTIFACT_ID, node);
        final String v = getStringProperty(VERSION, node);

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

        return new ProjectVersionRef(g, a, v);
    }

    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());
        relationship.setProperty(SOURCE_URI, rel.getSource().toString());
        relationship.setProperty(POM_LOCATION_URI, rel.getPomLocation().toString());

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

            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 ProjectRelationship<?> toProjectRelationship(final Relationship rel) {
        if (rel == null) {
            return null;
        }

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

        //        LOGGER.debug( "Converting relationship of type: %s (atlas type: %s)", 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;
        }

        final ProjectVersionRef from = toProjectVersionRef(rel.getStartNode());
        final ProjectVersionRef to = toProjectVersionRef(rel.getEndNode());
        final int index = getIntegerProperty(INDEX, rel);
        String uri = getStringProperty(SOURCE_URI, rel);
        URI source;
        try {
            source = new URI(uri);
        } catch (final URISyntaxException e) {
            source = UNKNOWN_SOURCE_URI;
        }

        uri = getStringProperty(POM_LOCATION_URI, rel);
        URI pomLocation;
        try {
            pomLocation = new URI(uri);
        } catch (final URISyntaxException e) {
            pomLocation = POM_ROOT_URI;
        }

        ProjectRelationship<?> result = null;
        switch (mapper.atlasType()) {
        case DEPENDENCY: {
            final ArtifactRef artifact = toArtifactRef(to, rel);
            final boolean managed = getBooleanProperty(IS_MANAGED, rel);
            final String scopeStr = getStringProperty(SCOPE, rel);
            final DependencyScope scope = DependencyScope.getScope(scopeStr);

            final String excludeStr = getStringProperty(EXCLUDES, rel);
            final Set<ProjectRef> excludes = new HashSet<ProjectRef>();
            if (excludeStr != null) {
                final String[] e = excludeStr.split("\\s*,\\s*");
                for (final String ex : e) {
                    final String[] parts = ex.split(":");
                    if (parts.length != 2) {
                        LOGGER.error("In: %s -> %s skipping invalid exclude specification: '%s'", from, artifact,
                                ex);
                    } else {
                        excludes.add(new ProjectRef(parts[0], parts[1]));
                    }
                }
            }

            result = new DependencyRelationship(source, pomLocation, from, artifact, scope, index, managed,
                    excludes.toArray(new ProjectRef[] {}));
            break;
        }
        case PLUGIN_DEP: {
            final ArtifactRef artifact = toArtifactRef(to, rel);
            final String pa = getStringProperty(PLUGIN_ARTIFACT_ID, rel);
            final String pg = getStringProperty(PLUGIN_GROUP_ID, rel);
            final boolean managed = getBooleanProperty(IS_MANAGED, rel);

            result = new PluginDependencyRelationship(source, pomLocation, from, new ProjectRef(pg, pa), artifact,
                    index, managed);
            break;
        }
        case PLUGIN: {
            final boolean managed = getBooleanProperty(IS_MANAGED, rel);
            final boolean reporting = getBooleanProperty(IS_REPORTING_PLUGIN, rel);

            result = new PluginRelationship(source, pomLocation, from, to, index, managed, reporting);
            break;
        }
        case EXTENSION: {
            result = new ExtensionRelationship(source, from, to, index);
            break;
        }
        case PARENT: {
            result = new ParentRelationship(source, from, to);
            break;
        }
        default: {
        }
        }

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

    public static String id(final ProjectRelationship<?> rel) {
        return DigestUtils.shaHex(rel.toString());
    }

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

        final String type = getStringProperty(TYPE, rel);
        final String classifier = getStringProperty(CLASSIFIER, rel);
        final boolean optional = getBooleanProperty(OPTIONAL, rel);

        return new ArtifactRef(ref, type, classifier, optional);
    }

    private static void toRelationshipProperties(final ArtifactRef target, final Relationship relationship) {
        relationship.setProperty(OPTIONAL, target.isOptional());
        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 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 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) {
        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) {
        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());
            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) {
        node.setProperty(CONNECTED, connected);
    }

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

                collapsed.addAll(cycle);
            }
        }

        for (final Path cycle : cycles) {
            if (existing.contains(cycle)) {
                continue;
            }

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

            boolean containsGivenRelationship = false;
            for (final Relationship r : cycle.relationships()) {
                collapsed.add(r.getId());
                if (r.getId() == 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<Set<Long>> getInjectedCycles(final Relationship relationship) {
        final Set<Set<Long>> cycles = new HashSet<Set<Long>>();

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

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

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

        return cycles;
    }

    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));
        }

        to.setProperty(CLONE_OF, from.getId());
    }

    public static boolean isCloneFor(final Relationship relationship, final Relationship original) {
        if (relationship.hasProperty(CLONE_OF)) {
            final long id = (Long) relationship.getProperty(CLONE_OF);
            return original.getId() == id;
        }

        return false;
    }

    public static void markSelectedFor(final Relationship relationship, final Node root) {
        LOGGER.debug("Marking selected: %s for root: %s", getStringProperty(GAV, relationship.getEndNode()),
                getStringProperty(GAV, root));
        addToIdListing(root.getId(), SELECTED_FOR, relationship);
        removeFromIdListing(root.getId(), DESELECTED_FOR, relationship);
    }

    public static void markDeselectedFor(final Relationship relationship, final Node root) {
        LOGGER.debug("Marking de-selected: %s for root: %s", getStringProperty(GAV, relationship.getEndNode()),
                getStringProperty(GAV, root));
        removeFromIdListing(root.getId(), SELECTED_FOR, relationship);
        addToIdListing(root.getId(), DESELECTED_FOR, relationship);
    }

    public static void removeSelectionAnnotationsFor(final Relationship relationship, final Node root) {
        LOGGER.debug("%s: removing ALL selection annotations for: %s",
                getStringProperty(GAV, relationship.getEndNode()), getStringProperty(GAV, root));
        removeFromIdListing(root.getId(), SELECTED_FOR, relationship);
        removeFromIdListing(root.getId(), DESELECTED_FOR, relationship);
    }

    public static boolean idListingContains(final String property, final Relationship relationship,
            final long... targets) {
        if (!relationship.hasProperty(property)) {
            return false;
        }

        final long[] ids = (long[]) relationship.getProperty(property, new long[] {});
        //        LOGGER.info( "Relationship: %s\nValue of property: %s is: %s", relationship, property,
        //                     relationship.getProperty( property ) );

        Arrays.sort(ids);
        for (final long target : targets) {
            if (Arrays.binarySearch(ids, target) > -1) {
                return true;
            }
        }

        return false;
    }

    public static boolean idListingContains(final String property, final Relationship relationship,
            final Collection<Node> targets) {
        if (!relationship.hasProperty(property)) {
            return false;
        }

        final long[] ids = (long[]) relationship.getProperty(property, new long[] {});
        //        LOGGER.info( "Relationship: %s\nValue of property: %s is: %s", relationship, property,
        //                     relationship.getProperty( property ) );

        Arrays.sort(ids);
        for (final Node target : targets) {
            if (Arrays.binarySearch(ids, target.getId()) > -1) {
                return true;
            }
        }

        return false;
    }

    private static void addToIdListing(final long target, final String property, final Relationship relationship) {
        final Set<Long> ids = new HashSet<Long>();
        boolean contains = false;
        for (final long id : (long[]) relationship.getProperty(property, new long[] {})) {
            if (id == target) {
                contains = true;
            }

            ids.add(id);
        }

        if (!contains) {
            ids.add(target);
            relationship.setProperty(property, ids.toArray(new Long[] {}));
        }
    }

    private static void removeFromIdListing(final long target, final String property,
            final Relationship relationship) {
        final Set<Long> ids = new HashSet<Long>();
        boolean changed = false;
        for (final long id : (long[]) relationship.getProperty(property, new long[] {})) {
            if (id == target) {
                LOGGER.debug("Found id: %d in property: %s.", target, property);
                changed = true;
            } else {
                ids.add(id);
            }
        }

        if (changed) {
            if (ids.isEmpty()) {
                LOGGER.debug("Removing empty property: %s from: %s", property, relationship);
                relationship.removeProperty(property);
            } else {
                LOGGER.debug("Modifying property: %s from: %s", property, relationship);
                relationship.setProperty(property, ids.toArray(new Long[] {}));
            }
        }
    }

    public static boolean isDeselectedFor(final Relationship relationship, final Node... roots) {
        final long[] ids = new long[roots.length];
        for (int i = 0; i < roots.length; i++) {
            final Node n = roots[i];
            ids[i] = n.getId();
        }

        return idListingContains(DESELECTED_FOR, relationship, ids);
    }

    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;
    }

}