/**
* 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.cartographer.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.cartographer.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.cartographer.graph.spi.neo4j.GraphAdmin;
import org.commonjava.cartographer.graph.spi.neo4j.GraphRelType;
import org.commonjava.cartographer.graph.spi.neo4j.NodeType;
import org.commonjava.cartographer.graph.spi.neo4j.model.AbstractNeoProjectRelationship;
import org.commonjava.cartographer.graph.spi.neo4j.model.CyclePath;
import org.commonjava.cartographer.graph.spi.neo4j.model.NeoArtifactRef;
import org.commonjava.cartographer.graph.spi.neo4j.model.NeoBomRelationship;
import org.commonjava.cartographer.graph.spi.neo4j.model.NeoDependencyRelationship;
import org.commonjava.cartographer.graph.spi.neo4j.model.NeoExtensionRelationship;
import org.commonjava.cartographer.graph.spi.neo4j.model.NeoParentRelationship;
import org.commonjava.cartographer.graph.spi.neo4j.model.NeoPluginDependencyRelationship;
import org.commonjava.cartographer.graph.spi.neo4j.model.NeoPluginRelationship;
import org.commonjava.cartographer.graph.spi.neo4j.model.NeoProjectVersionRef;
import org.commonjava.cartographer.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 );
}
}