Skip to content

Commit

Permalink
Merge pull request #53 from marcphilipp/highlight-strong-connections-…
Browse files Browse the repository at this point in the history
…squashed

Highlight strong connections (squashed)
  • Loading branch information
marcphilipp committed Oct 26, 2015
2 parents 37151a0 + 69f81fd commit a4eebdd
Show file tree
Hide file tree
Showing 20 changed files with 689 additions and 75 deletions.
84 changes: 84 additions & 0 deletions org.projectusus.core.test/Usus_Dev.launch

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,23 +1,44 @@
package org.projectusus.core.filerelations.model;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;

import org.jgrapht.alg.StrongConnectivityInspector;

import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.primitives.Ints;

public class PackageRelations extends Relations<Packagename> {

private PackageCycles packageCycles;

private int maxLinkCount = -1;

public PackageRelations() {
calcPackageRelations();
calcPackageRelations( Predicates.<Packagename> alwaysTrue() );
calcPackageCycles();
}

private void calcPackageRelations() {
public PackageRelations( Predicate<Packagename> isVisible ) {
calcPackageRelations( isVisible );
calcPackageCycles();
}

public void calcPackageRelations( Predicate<Packagename> isVisible ) {
for( ClassDescriptor source : ClassDescriptor.getAll() ) {
for( ClassDescriptor target : source.getChildrenInOtherPackages() ) {
this.add( source.getPackagename(), target.getPackagename() );
if( isVisible.apply( source.getPackagename() ) && isVisible.apply( target.getPackagename() ) ) {
this.add( source.getPackagename(), target.getPackagename() );
}
}
}
}
Expand Down Expand Up @@ -47,4 +68,56 @@ public Set<Packagename> getDirectPackageRelationsTo( Packagename packagename ) {
public PackageCycles getPackageCycles() {
return packageCycles;
}

public int getCrossLinkCount( Packagename source, Packagename target ) {
int crossLinkCount = 0;
Collection<Relation<Packagename>> relationsFromSource = getAllOutgoingRelationsFrom( source );
for( Relation<Packagename> relation : relationsFromSource ) {
if( relation.getTarget().equals( target ) ) {
crossLinkCount++;
}
}
return crossLinkCount;
}

public int getMaxCrossLinkCount() {
if( maxCrossLinkCountCached() ) {
return maxLinkCount;
}

for( Packagename sourcePackage : outgoingRelations.keys() ) {
maxLinkCount = Math.max( maxLinkCount, calculateMaxLinkCountForPackage( sourcePackage ) );
}
return maxLinkCount;
}

private boolean maxCrossLinkCountCached() {
return maxLinkCount > -1;
}

private int calculateMaxLinkCountForPackage( Packagename sourcePackage ) {
Multimap<Packagename, Relation<Packagename>> groupedLinks = Multimaps.index( outgoingRelations.get( sourcePackage ), Relation.<Packagename> target() );

Packagename mostTargetedPackage = findMostTargetedPackage( groupedLinks );
int maxNumberOfRelations = groupedLinks.get( mostTargetedPackage ).size();

return maxNumberOfRelations;
}

private Packagename findMostTargetedPackage( Multimap<Packagename, Relation<Packagename>> linksByPackage ) {
List<Entry<Packagename, Relation<Packagename>>> entries = new ArrayList<Entry<Packagename, Relation<Packagename>>>( linksByPackage.entries() );
Packagename mostTargetedPackage = Collections.max( entries, mapValuesComparator( linksByPackage ) ).getKey();
return mostTargetedPackage;
}

private Comparator<Entry<Packagename, Relation<Packagename>>> mapValuesComparator( final Multimap<Packagename, Relation<Packagename>> linksByPackage ) {
return new Comparator<Entry<Packagename, Relation<Packagename>>>() {
public int compare( Entry<Packagename, Relation<Packagename>> e1, Entry<Packagename, Relation<Packagename>> e2 ) {
Collection<Relation<Packagename>> relationsToPackage1 = linksByPackage.get( e1.getKey() );
Collection<Relation<Packagename>> relationsToPackage2 = linksByPackage.get( e2.getKey() );
return Ints.compare( relationsToPackage1.size(), relationsToPackage2.size() );
}
};
}

}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package org.projectusus.core.filerelations.model;

import net.sourceforge.c4j.*;
import net.sourceforge.c4j.ContractReference;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;

@ContractReference(contractClassName = "RelationContract")
import com.google.common.base.Function;

@ContractReference( contractClassName = "RelationContract" )
public class Relation<T> {

protected final T source;
Expand Down Expand Up @@ -50,4 +52,11 @@ public String toString() {
return source + " -> " + target; //$NON-NLS-1$
}

public final static <T> Function<Relation<T>, T> target() {
return new Function<Relation<T>, T>() {
public T apply( Relation<T> input ) {
return input.target;
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,40 @@

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import net.sourceforge.c4j.ContractReference;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;

@ContractReference( contractClassName = "RelationsContract" )
public class Relations<K> {

protected final SetMultimap<K, Relation<K>> incomingRelations;
protected final SetMultimap<K, Relation<K>> outgoingRelations;
protected final Multimap<K, Relation<K>> incomingRelations;
protected final Multimap<K, Relation<K>> outgoingRelations;

public Relations() {
incomingRelations = HashMultimap.create();
outgoingRelations = HashMultimap.create();
incomingRelations = ArrayListMultimap.create();
outgoingRelations = ArrayListMultimap.create();
}

public Set<Relation<K>> getDirectRelationsFrom( K sourceFile ) {
return outgoingRelations.get( sourceFile );
return getAsSet( outgoingRelations.get( sourceFile ) );
}

public Set<Relation<K>> getDirectRelationsTo( K targetFile ) {
return incomingRelations.get( targetFile );
return getAsSet( incomingRelations.get( targetFile ) );
}

public void removeDirectRelationsFrom( K sourceFile ) {
outgoingRelations.removeAll( sourceFile );
}

public Collection<Relation<K>> getAllDirectRelations() {
return Collections.unmodifiableCollection( outgoingRelations.values() );
return Collections.unmodifiableSet( getAsSet( outgoingRelations.values() ) );
}

protected void add( K sourceKey, K targetKey ) {
Expand All @@ -58,11 +60,15 @@ public Set<K> keySet() {
return union( outgoingRelations.keySet(), incomingRelations.keySet() );
}

protected Set<Relation<K>> getOutgoingRelationsFrom( K key ) {
protected Collection<Relation<K>> getAllOutgoingRelationsFrom( K key ) {
return outgoingRelations.get( key );
}

protected Set<Relation<K>> getIncomingRelationsTo( K key ) {
protected Collection<Relation<K>> getAllIncomingRelationsTo( K key ) {
return incomingRelations.get( key );
}

private HashSet<Relation<K>> getAsSet( Collection<Relation<K>> collection ) {
return Sets.newHashSet( collection );
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package org.projectusus.core.filerelations.model.test;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;

import org.eclipse.core.resources.IFile;
import org.junit.Before;
import org.junit.Test;
import org.projectusus.core.filerelations.model.ClassDescriptor;
import org.projectusus.core.filerelations.model.Classname;
import org.projectusus.core.filerelations.model.PackageRelations;
import org.projectusus.core.filerelations.model.Packagename;
import org.projectusus.core.statistics.UsusModelProvider;

public class PackageRelationsTest {

private static Packagename I = Packagename.of( "I", null ); //$NON-NLS-1$
private static Packagename II = Packagename.of( "II", null ); //$NON-NLS-1$
private static Packagename III = Packagename.of( "III", null ); //$NON-NLS-1$

private ClassDescriptor I_A;
private ClassDescriptor I_B;
private ClassDescriptor II_A;
private ClassDescriptor II_B;
private ClassDescriptor III_A;

@Before
public void setup() {
UsusModelProvider.clear();
I_A = createDescriptor( I );
I_B = createDescriptor( I );
II_A = createDescriptor( II );
II_B = createDescriptor( II );
III_A = createDescriptor( III );
}

@Test
public void crossLinkCountIsDirected() {
I_A.addChild( II_A );
I_A.addChild( I_B );
II_A.addChild( II_B );

assertEquals( 1, getCrossLinkCount( I, II ) );
assertEquals( 0, getCrossLinkCount( II, I ) );
}

@Test
public void noCrossLinksInsidePackages() {
I_A.addChild( I_B );

assertEquals( 0, getCrossLinkCount( I, I ) );
}

@Test
public void crossLinkCountAddsUp() {
I_A.addChild( II_A );
I_B.addChild( II_A );
I_A.addChild( II_B );
I_B.addChild( II_B );

assertEquals( 4, getCrossLinkCount( I, II ) );
}

@Test
public void referencesAmongTwoClassesCountOnce() {
I_A.addChild( II_A );
I_A.addChild( II_A );

assertEquals( 1, getCrossLinkCount( I, II ) );
}

@Test
public void maxLinkCount() {
I_A.addChild( II_A );
I_A.addChild( II_B );
I_A.addChild( III_A );
II_A.addChild( I_A );
II_A.addChild( I_B );
III_A.addChild( II_A );

assertEquals( 2, new PackageRelations().getMaxCrossLinkCount() );
}

private int getCrossLinkCount( Packagename source, Packagename target ) {
return new PackageRelations().getCrossLinkCount( source, target );
}

private static ClassDescriptor createDescriptor( Packagename packagename ) {
return ClassDescriptor.of( mock( IFile.class ), new Classname( "classname1" ), packagename ); //$NON-NLS-1$
}

}
2 changes: 2 additions & 0 deletions org.projectusus.ui.dependencygraph/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ Export-Package: org.projectusus.ui.dependencygraph;x-internal:=true,
org.projectusus.ui.dependencygraph.common;x-internal:=true,
org.projectusus.ui.dependencygraph.filters;x-internal:=true,
org.projectusus.ui.dependencygraph.handlers;x-internal:=true
Bundle-ClassPath: .,
lib/guava-r06.jar
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.projectusus.ui.dependencygraph.colorProvider;

import static org.projectusus.ui.colors.UsusColors.DARK_GREY;
import static org.projectusus.ui.colors.UsusColors.DARK_RED;
import static org.projectusus.ui.colors.UsusColors.getSharedColors;
import static org.projectusus.ui.dependencygraph.nodes.NodeLabelProvider.isCrossPackageRelation;

import java.util.Set;

import org.eclipse.swt.graphics.Color;
import org.projectusus.core.filerelations.model.Packagename;
import org.projectusus.ui.dependencygraph.nodes.IEdgeColorProvider;

public class ClassEdgeColorProvider implements IEdgeColorProvider {

public Color getEdgeColor( Object src, Object dest, boolean highlightStrongConnections ) {
if( isCrossPackageRelation( src, dest ) ) {
return getSharedColors().getColor( DARK_RED );
}
return getSharedColors().getColor( DARK_GREY );
}

public void recalculateColors( Set<Packagename> visibleNodes ) {
// nothing to refresh
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package org.projectusus.ui.dependencygraph.colorProvider;

import static org.projectusus.ui.colors.UsusColors.DARK_RED;
import static org.projectusus.ui.colors.UsusColors.getSharedColors;

import java.util.Set;

import org.eclipse.swt.graphics.Color;
import org.projectusus.core.filerelations.model.PackageRelations;
import org.projectusus.core.filerelations.model.Packagename;
import org.projectusus.ui.dependencygraph.nodes.IEdgeColorProvider;
import org.projectusus.ui.dependencygraph.nodes.PackageRepresenter;

import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;

public class PackageEdgeColorProvider implements IEdgeColorProvider {

private Supplier<PackageRelations> packageRelationsSupplier;
private Predicate<Packagename> isVisible;

public PackageEdgeColorProvider() {
isVisible = Predicates.<Packagename> alwaysTrue();
calculatePackageRelations();
}

public Color getEdgeColor( Object src, Object dest, boolean highlightStrongConnections ) {
if( highlightStrongConnections ) {
float saturation = computeSaturation( src, dest );
return getSharedColors().adjustSaturation( DARK_RED, saturation );
}
return getSharedColors().getColor( DARK_RED );
}

private float computeSaturation( Object src, Object dest ) {
PackageRelations packageRelations = packageRelationsSupplier.get();

int crossLinkCount = getCrossLinkCount( (PackageRepresenter)src, (PackageRepresenter)dest, packageRelations );
float maxCrossLinkCount = packageRelations.getMaxCrossLinkCount();
float saturation = crossLinkCount / maxCrossLinkCount;
return saturation;
}

private int getCrossLinkCount( PackageRepresenter src, PackageRepresenter target, PackageRelations packageRelations ) {
return packageRelations.getCrossLinkCount( src.getPackagename(), target.getPackagename() );
}

public void recalculateColors( final Set<Packagename> visiblePackages ) {
isVisible = new Predicate<Packagename>() {
public boolean apply( Packagename packagename ) {
return visiblePackages.contains( packagename );
}
};
calculatePackageRelations();
}

public void calculatePackageRelations() {
packageRelationsSupplier = Suppliers.memoize( new Supplier<PackageRelations>() {
public PackageRelations get() {
return new PackageRelations( isVisible );
}
} );
}
}
Loading

0 comments on commit a4eebdd

Please sign in to comment.