diff --git a/pom.xml b/pom.xml index 1a37142e64dcde75bec6b23cb852f4fcb0089deb..05d1ca3a064ed4aec8cf691471fb7f6185e84c04 100644 --- a/pom.xml +++ b/pom.xml @@ -56,5 +56,12 @@ <artifactId>reflections</artifactId> <version>0.9.10</version> </dependency> + <!-- https://mvnrepository.com/artifact/org.gephi/gephi-toolkit --> + <dependency> + <groupId>org.gephi</groupId> + <artifactId>gephi-toolkit</artifactId> + <version>0.9.1</version> + </dependency> + </dependencies> </project> \ No newline at end of file diff --git a/src/main/java/fucoin/actions/control/ActionAddOverlayNeighbours.java b/src/main/java/fucoin/actions/control/ActionAddOverlayNeighbours.java new file mode 100644 index 0000000000000000000000000000000000000000..890b6ec06ec9a938bc086a79047d4c8973df6b19 --- /dev/null +++ b/src/main/java/fucoin/actions/control/ActionAddOverlayNeighbours.java @@ -0,0 +1,26 @@ +package fucoin.actions.control; + +import akka.actor.ActorRef; +import akka.actor.UntypedActorContext; +import fucoin.actions.ClientAction; +import fucoin.wallet.AbstractWallet; + +import java.util.Collections; +import java.util.List; + +/** + * @author davidbohn + */ +public class ActionAddOverlayNeighbours extends ClientAction { + + protected List<ActorRef> neighbours; + + public ActionAddOverlayNeighbours(List<ActorRef> neighbours) { + this.neighbours = neighbours; + } + + @Override + protected void onAction(ActorRef sender, ActorRef self, UntypedActorContext context, AbstractWallet abstractNode) { + neighbours.stream().forEach(abstractNode::addOverlayNeighbour); + } +} diff --git a/src/main/java/fucoin/configurations/AbstractConfiguration.java b/src/main/java/fucoin/configurations/AbstractConfiguration.java index 8493704af98937d47688ad0b57323902394e0d8d..07fcb48451e5796bf83e283500c301da0cde74d3 100644 --- a/src/main/java/fucoin/configurations/AbstractConfiguration.java +++ b/src/main/java/fucoin/configurations/AbstractConfiguration.java @@ -14,13 +14,12 @@ import fucoin.actions.transaction.ActionNotifyObserver; import fucoin.configurations.internal.ConfigurationCreator; import fucoin.supervisor.SuperVisorImpl; import fucoin.wallet.WalletImpl; +import org.gephi.graph.api.Node; import scala.concurrent.Await; import scala.concurrent.Future; import scala.concurrent.duration.Duration; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.util.*; import java.util.concurrent.ThreadLocalRandom; /** @@ -30,12 +29,14 @@ public abstract class AbstractConfiguration extends AbstractNode { private ActorRef superVisor; - private final List<ActorRef> activeActors = new ArrayList<>(); + private final HashMap<String, ActorRef> activeActors = new HashMap<>(); private Timeout timeout = new Timeout(Duration.create(10, "seconds")); private int remainingTransactions; + private ActorRef lastWallet = null; + public static Props props(Class configurationClass) { return Props.create(new ConfigurationCreator(configurationClass)); @@ -58,21 +59,18 @@ public abstract class AbstractConfiguration extends AbstractNode { */ private ActorRef createWallet(String name, boolean createGUI) { Props props; - int numOfWallets = activeActors.size(); - if (numOfWallets == 0) { - props = WalletImpl.props(null, name, createGUI); - } else { - props = WalletImpl.props(activeActors.get(numOfWallets - 1), name, createGUI); - } - ActorRef actorRef = context().actorOf(props, name); + props = WalletImpl.props(lastWallet, name, createGUI); - activeActors.add(actorRef); + ActorRef actorRef = context().actorOf(props, name); + activeActors.put(name, actorRef); - if (numOfWallets == 0) { + if (lastWallet == null) { actorRef.tell(new ActionTellSupervisor(superVisor), superVisor); } + lastWallet = actorRef; + return actorRef; } @@ -90,6 +88,15 @@ public abstract class AbstractConfiguration extends AbstractNode { Await.result(future, timeout.duration()); } + public void spawnWalletsFromNodes(Collection<Node> nodes, boolean createGUI) throws Exception { + Future<Object> future = Patterns.ask(superVisor, new ActionAnnounceWalletCreation(nodes.size(), self()), timeout); + for (Node node : nodes) { + String nameOfTheWallet = node.getLabel(); + createWallet(nameOfTheWallet, createGUI); + } + Await.result(future, timeout.duration()); + } + /** * Fetch a random wallet */ @@ -98,7 +105,11 @@ public abstract class AbstractConfiguration extends AbstractNode { } public List<ActorRef> wallets() { - return this.activeActors; + return new ArrayList<>(this.activeActors.values()); + } + + public ActorRef walletByName(String name) { + return activeActors.get(name); } protected void randomTransactions(int number, int maxTransactionsAtTheSameTime) { diff --git a/src/main/java/fucoin/configurations/DefaultConfiguration.java b/src/main/java/fucoin/configurations/DefaultConfiguration.java index 403d071f351c51d413d9e38fd07331f52625a13f..6b5a3565cae995040e504ae510eef27c74a2b158 100644 --- a/src/main/java/fucoin/configurations/DefaultConfiguration.java +++ b/src/main/java/fucoin/configurations/DefaultConfiguration.java @@ -1,19 +1,11 @@ package fucoin.configurations; import akka.actor.ActorRef; -import akka.pattern.Patterns; import akka.util.Timeout; import fucoin.actions.control.ActionWalletSendMoney; -import fucoin.actions.transaction.ActionGetAmount; -import fucoin.actions.transaction.ActionGetAmountAnswer; -import fucoin.actions.transaction.ActionNotifyObserver; import fucoin.configurations.internal.ConfigurationName; -import scala.concurrent.Await; -import scala.concurrent.Future; import scala.concurrent.duration.Duration; -import java.util.Collections; -import java.util.List; import java.util.concurrent.ThreadLocalRandom; /** diff --git a/src/main/java/fucoin/configurations/GephiConfiguration.java b/src/main/java/fucoin/configurations/GephiConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..cc01d898b50fb5d048c3ab44c6c95cd16e695348 --- /dev/null +++ b/src/main/java/fucoin/configurations/GephiConfiguration.java @@ -0,0 +1,78 @@ +package fucoin.configurations; + +import akka.actor.ActorRef; +import fucoin.actions.control.ActionAddOverlayNeighbours; +import fucoin.configurations.internal.ConfigurationName; +import fucoin.gui.gephi.GraphWindow; +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.GraphController; +import org.gephi.graph.api.Node; +import org.gephi.io.importer.api.Container; +import org.gephi.io.importer.api.ImportController; +import org.gephi.io.processor.plugin.DefaultProcessor; +import org.gephi.project.api.ProjectController; +import org.gephi.project.api.Workspace; +import org.openide.util.Lookup; + +import java.io.File; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +@ConfigurationName("Gephi Test Configuration") +public class GephiConfiguration extends AbstractConfiguration { + @Override + public void run() { + initSupervisor(); + + ProjectController pc = Lookup.getDefault().lookup(ProjectController.class); + + pc.newProject(); + Workspace workspace = pc.getCurrentWorkspace(); + + Container container; + ImportController importController = Lookup.getDefault().lookup(ImportController.class); + try { + File file = new File(getClass().getResource("/topology2.gexf").toURI()); + container = importController.importFile(file); + } catch (Exception ex) { + ex.printStackTrace(); + return; + } + + importController.process(container, new DefaultProcessor(), workspace); + + Graph g = Lookup.getDefault().lookup(GraphController.class).getGraphModel(workspace).getGraph(); + + Collection<Node> nodes = g.getNodes().toCollection(); + + try { + spawnWalletsFromNodes(nodes, false); + } catch (Exception e) { + e.printStackTrace(); + } + + nodes.stream().forEach(node -> { + ActorRef wallet = walletByName(node.getLabel()); + Edge[] edges = g.getEdges(node).toArray(); + + // Search for all reachable neighbours of the node + // by filtering the list of incident edges of the node + // and retrieve the respective ActorRef instance + List<ActorRef> overlayNeighbours = Arrays.stream(edges).filter(edge -> !edge.isDirected() || edge.getSource() == node).map(edge -> { + if (edge.getSource() == node) { + return edge.getTarget(); + } + return edge.getSource(); + }).map(target -> walletByName(target.getLabel())).collect(Collectors.toList()); + + wallet.tell(new ActionAddOverlayNeighbours(overlayNeighbours), self()); + }); + + GraphWindow graphWindow = new GraphWindow(); + + graphWindow.setVisible(true); + } +} diff --git a/src/main/java/fucoin/gui/gephi/GraphWindow.java b/src/main/java/fucoin/gui/gephi/GraphWindow.java new file mode 100644 index 0000000000000000000000000000000000000000..04cdc06d4d4d8a0a6440502e7e9d8bcab265e744 --- /dev/null +++ b/src/main/java/fucoin/gui/gephi/GraphWindow.java @@ -0,0 +1,65 @@ +package fucoin.gui.gephi; + +import org.gephi.preview.api.*; +import org.gephi.preview.types.DependantOriginalColor; +import org.openide.util.Lookup; + +import javax.swing.*; +import java.awt.*; +import java.lang.reflect.Field; + +public class GraphWindow extends JFrame { + public GraphWindow() { + super(); + + setTitle("Network Overlay Graph"); + + setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + + PreviewController previewController = Lookup.getDefault().lookup(PreviewController.class); + PreviewModel previewModel = previewController.getModel(); + previewModel.getProperties().putValue(PreviewProperty.SHOW_NODE_LABELS, Boolean.TRUE); + previewModel.getProperties().putValue(PreviewProperty.NODE_LABEL_COLOR, new DependantOriginalColor(Color.BLACK)); + + Font labelFont = new Font("Verdana", Font.PLAIN, 5); + previewModel.getProperties().putValue(PreviewProperty.NODE_LABEL_FONT, labelFont); + previewModel.getProperties().putValue(PreviewProperty.EDGE_CURVED, Boolean.FALSE); + previewModel.getProperties().putValue(PreviewProperty.EDGE_OPACITY, 50); + previewModel.getProperties().putValue(PreviewProperty.EDGE_RADIUS, 10f); + + G2DTarget target = (G2DTarget) previewController.getRenderTarget(RenderTarget.G2D_TARGET); + PreviewSketch previewSketch = new PreviewSketch(target, isRetina()); + + this.add(previewSketch, BorderLayout.CENTER); + + previewController.refreshPreview(); + previewSketch.resetZoom(); + + + this.setSize(800, 600); + } + + public static boolean isRetina() { + //TODO: OS and JVM specific checks... + //if (SystemInfo.isJavaVersionAtLeast("1.7.0_40") && SystemInfo.isOracleJvm) { + GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); + final GraphicsDevice device = env.getDefaultScreenDevice(); + + try { + Field field = device.getClass().getDeclaredField("scale"); + + if (field != null) { + field.setAccessible(true); + Object scale = field.get(device); + + if (scale instanceof Integer && ((Integer) scale).intValue() == 2) { + return true; + } + } + } catch (Exception ignore) { + } + //} + //... + return false; + } +} diff --git a/src/main/java/fucoin/gui/gephi/ItemBuilderTemplate.java b/src/main/java/fucoin/gui/gephi/ItemBuilderTemplate.java new file mode 100644 index 0000000000000000000000000000000000000000..2d62f90e427e821dc3523be2e00a48e472d15d23 --- /dev/null +++ b/src/main/java/fucoin/gui/gephi/ItemBuilderTemplate.java @@ -0,0 +1,27 @@ +package fucoin.gui.gephi; + +import org.gephi.graph.api.Edge; +import org.gephi.graph.api.Graph; +import org.gephi.graph.api.Node; +import org.gephi.preview.api.Item; +import org.gephi.preview.spi.ItemBuilder; +import org.openide.util.lookup.ServiceProvider; + +import java.util.Arrays; + +@ServiceProvider(service = ItemBuilder.class) +public class ItemBuilderTemplate implements ItemBuilder { + @Override + public Item[] getItems(Graph graph) { + Node n = graph.getNodes().toArray()[0]; + Edge[] edges = graph.getEdges(n).toArray(); + Arrays.stream(edges).filter(edge -> edge.getSource() == n).map(Edge::getTarget); + //return new Item[]{new LabelItem(n)}; + return new Item[0]; + } + + @Override + public String getType() { + return null; + } +} diff --git a/src/main/java/fucoin/gui/gephi/LabelItem.java b/src/main/java/fucoin/gui/gephi/LabelItem.java new file mode 100644 index 0000000000000000000000000000000000000000..b4a123309ab4af6d32e452a2d255e18442ebf9e0 --- /dev/null +++ b/src/main/java/fucoin/gui/gephi/LabelItem.java @@ -0,0 +1,36 @@ +package fucoin.gui.gephi; + +import org.gephi.graph.api.Node; +import org.gephi.preview.api.Item; + +public class LabelItem implements Item { + Node node; + + public LabelItem(Node node) { + this.node = node; + } + + @Override + public Object getSource() { + return node; + } + + @Override + public String getType() { + return "label.sometype"; + } + + @Override + public <D> D getData(String key) { + return null; + } + + @Override + public void setData(String key, Object value) { + } + + @Override + public String[] getKeys() { + return new String[0]; + } +} diff --git a/src/main/java/fucoin/gui/gephi/MouseListenerTemplate.java b/src/main/java/fucoin/gui/gephi/MouseListenerTemplate.java new file mode 100644 index 0000000000000000000000000000000000000000..99af5066b949f71fe50f2f7800cc7fe4739f5451 --- /dev/null +++ b/src/main/java/fucoin/gui/gephi/MouseListenerTemplate.java @@ -0,0 +1,51 @@ +package fucoin.gui.gephi; + +import org.gephi.graph.api.GraphController; +import org.gephi.graph.api.Node; +import org.gephi.preview.api.PreviewMouseEvent; +import org.gephi.preview.api.PreviewProperties; +import org.gephi.preview.spi.PreviewMouseListener; +import org.gephi.project.api.Workspace; +import org.openide.util.Lookup; +import org.openide.util.lookup.ServiceProvider; + +import javax.swing.*; + +@ServiceProvider(service = PreviewMouseListener.class) +public class MouseListenerTemplate implements PreviewMouseListener { + @Override + public void mouseClicked(PreviewMouseEvent event, PreviewProperties properties, Workspace workspace) { + for (Node node : Lookup.getDefault().lookup(GraphController.class).getGraphModel(workspace).getGraph().getNodes()) { + if (clickingInNode(node, event)) { + properties.putValue("display-label.node.id", node.getId()); + System.err.println("Node " + node.getLabel() + " clicked!");//System.out is ignored in Netbeans platform applications!! + JOptionPane.showMessageDialog(null, "Node " + node.getLabel() + " clicked!"); + event.setConsumed(true);//So the renderer is executed and the graph repainted + return; + } + } + + properties.removeSimpleValue("display-label.node.id"); + event.setConsumed(true);//So the renderer is executed and the graph repainted + } + + @Override + public void mousePressed(PreviewMouseEvent event, PreviewProperties properties, Workspace workspace) { + } + + @Override + public void mouseDragged(PreviewMouseEvent event, PreviewProperties properties, Workspace workspace) { + } + + @Override + public void mouseReleased(PreviewMouseEvent event, PreviewProperties properties, Workspace workspace) { + } + + private boolean clickingInNode(Node node, PreviewMouseEvent event) { + float xdiff = node.x() - event.x; + float ydiff = -node.y() - event.y;//Note that y axis is inverse for node coordinates + float radius = node.size(); + + return xdiff * xdiff + ydiff * ydiff < radius * radius; + } +} diff --git a/src/main/java/fucoin/gui/gephi/PreviewSketch.java b/src/main/java/fucoin/gui/gephi/PreviewSketch.java new file mode 100644 index 0000000000000000000000000000000000000000..40db173c880fb6a38d21062026b6e24b372fa940 --- /dev/null +++ b/src/main/java/fucoin/gui/gephi/PreviewSketch.java @@ -0,0 +1,229 @@ +package fucoin.gui.gephi; + +import java.awt.Graphics; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.atomic.AtomicBoolean; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import org.gephi.preview.api.G2DTarget; +import org.gephi.preview.api.PreviewController; +import org.gephi.preview.api.PreviewMouseEvent; +import org.gephi.preview.api.Vector; +import org.openide.util.Lookup; + +public class PreviewSketch extends JPanel implements MouseListener, MouseWheelListener, MouseMotionListener { + private static final int WHEEL_TIMER = 500; + //Data + private final PreviewController previewController; + private final G2DTarget target; + //Geometry + private final Vector ref = new Vector(); + private final Vector lastMove = new Vector(); + //Utils + private final RefreshLoop refreshLoop = new RefreshLoop(); + private Timer wheelTimer; + private boolean inited; + private final boolean isRetina; + + public PreviewSketch(G2DTarget target, boolean retina) { + this.target = target; + previewController = Lookup.getDefault().lookup(PreviewController.class); + isRetina = retina; + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + + if (!inited) { + //Listeners + addMouseListener(this); + addMouseMotionListener(this); + addMouseWheelListener(this); + inited = true; + } + + int width = (int) (getWidth() * (isRetina ? 2.0 : 1.0)); + int height = (int) (getHeight() * (isRetina ? 2.0 : 1.0)); + + if (target.getWidth() != width || target.getHeight() != height) { + target.resize(width, height); + } + + g.drawImage(target.getImage(), 0, 0, getWidth(), getHeight(), this); + } + + public void setMoving(boolean moving) { + target.setMoving(moving); + } + + @Override + public void mouseClicked(MouseEvent e) { + if (previewController.sendMouseEvent(buildPreviewMouseEvent(e, PreviewMouseEvent.Type.CLICKED))) { + refreshLoop.refreshSketch(); + } + } + + @Override + public void mousePressed(MouseEvent e) { + previewController.sendMouseEvent(buildPreviewMouseEvent(e, PreviewMouseEvent.Type.PRESSED)); + ref.set(e.getX(), e.getY()); + lastMove.set(target.getTranslate()); + + refreshLoop.refreshSketch(); + } + + @Override + public void mouseReleased(MouseEvent e) { + if (!previewController.sendMouseEvent(buildPreviewMouseEvent(e, PreviewMouseEvent.Type.RELEASED))) { + setMoving(false); + } + + refreshLoop.refreshSketch(); + } + + @Override + public void mouseEntered(MouseEvent e) { + } + + @Override + public void mouseExited(MouseEvent e) { + } + + @Override + public void mouseWheelMoved(MouseWheelEvent e) { + if (e.getUnitsToScroll() == 0) { + return; + } + float way = -e.getUnitsToScroll() / Math.abs(e.getUnitsToScroll()); + target.setScaling(target.getScaling() * (way > 0 ? 2f : 0.5f)); + setMoving(true); + if (wheelTimer != null) { + wheelTimer.cancel(); + wheelTimer = null; + } + wheelTimer = new Timer(); + wheelTimer.schedule(new TimerTask() { + @Override + public void run() { + setMoving(false); + refreshLoop.refreshSketch(); + wheelTimer = null; + } + }, WHEEL_TIMER); + refreshLoop.refreshSketch(); + } + + @Override + public void mouseDragged(MouseEvent e) { + if (!previewController.sendMouseEvent(buildPreviewMouseEvent(e, PreviewMouseEvent.Type.DRAGGED))) { + setMoving(true); + Vector trans = target.getTranslate(); + trans.set(e.getX(), e.getY()); + trans.sub(ref); + trans.mult(isRetina ? 2f : 1f); + trans.div(target.getScaling()); // ensure const. moving speed whatever the zoom is + trans.add(lastMove); + + refreshLoop.refreshSketch(); + } + } + + @Override + public void mouseMoved(MouseEvent e) { + } + + public void zoomPlus() { + target.setScaling(target.getScaling() * 2f); + refreshLoop.refreshSketch(); + } + + public void zoomMinus() { + target.setScaling(target.getScaling() / 2f); + refreshLoop.refreshSketch(); + } + + public void resetZoom() { + target.reset(); + refreshLoop.refreshSketch(); + } + + private Vector screenPositionToModelPosition(Vector screenPos) { + Vector center = new Vector(getWidth() / 2f, getHeight() / 2f); + Vector scaledCenter = Vector.mult(center, target.getScaling()); + Vector scaledTrans = Vector.sub(center, scaledCenter); + + Vector modelPos = new Vector(screenPos.x, screenPos.y); + modelPos.sub(scaledTrans); + modelPos.div(target.getScaling()); + modelPos.sub(target.getTranslate()); + return modelPos; + } + + private PreviewMouseEvent buildPreviewMouseEvent(MouseEvent evt, PreviewMouseEvent.Type type) { + int mouseX = evt.getX(); + int mouseY = evt.getY(); + PreviewMouseEvent.Button button = PreviewMouseEvent.Button.LEFT; + if (SwingUtilities.isMiddleMouseButton(evt)) { + button = PreviewMouseEvent.Button.MIDDLE; + } else if (SwingUtilities.isLeftMouseButton(evt)) { + button = PreviewMouseEvent.Button.LEFT; + } else if (SwingUtilities.isRightMouseButton(evt)) { + button = PreviewMouseEvent.Button.RIGHT; + } + + Vector pos = screenPositionToModelPosition(new Vector(mouseX, mouseY)); + + return new PreviewMouseEvent((int) pos.x, (int) pos.y, type, button, null); + } + + private class RefreshLoop { + + private final long DELAY = 100; + private final AtomicBoolean running = new AtomicBoolean(); + private final AtomicBoolean refresh = new AtomicBoolean(); + //Timer + private long timeout = DELAY * 10; + private Timer timer; + + public RefreshLoop() { + super(); + } + + public void refreshSketch() { + refresh.set(true); + if (!running.getAndSet(true)) { + startTimer(); + } + } + + private void startTimer() { + timer = new Timer("PreviewRefreshLoop", true); + timer.schedule(new TimerTask() { + @Override + public void run() { + if (refresh.getAndSet(false)) { + target.refresh(); + repaint(); + } else if (timeout == 0) { + timeout = DELAY * 10; + stopTimer(); + } else { + timeout -= DELAY; + } + } + }, 0, DELAY); + } + + private void stopTimer() { + timer.cancel(); + running.set(false); + } + } +} diff --git a/src/main/java/fucoin/gui/gephi/RendererTemplate.java b/src/main/java/fucoin/gui/gephi/RendererTemplate.java new file mode 100644 index 0000000000000000000000000000000000000000..20dc753cc854020e39b7123571e3dc7ffd80ba5e --- /dev/null +++ b/src/main/java/fucoin/gui/gephi/RendererTemplate.java @@ -0,0 +1,65 @@ +package fucoin.gui.gephi; + +import org.gephi.graph.api.Node; +import org.gephi.preview.api.*; +import org.gephi.preview.spi.ItemBuilder; +import org.gephi.preview.spi.MouseResponsiveRenderer; +import org.gephi.preview.spi.PreviewMouseListener; +import org.gephi.preview.spi.Renderer; +import org.openide.util.lookup.ServiceProvider; + +import java.awt.*; + +@ServiceProvider(service = Renderer.class) +public class RendererTemplate implements Renderer, MouseResponsiveRenderer { + @Override + public String getDisplayName() { + return "Some name"; + } + + @Override + public void preProcess(PreviewModel previewModel) { + } + + @Override + public void render(Item item, RenderTarget target, PreviewProperties properties) { + //Retrieve clicked node for the label: + LabelItem label = (LabelItem) item; + Node node = label.node; + System.out.println("Rendering"); + + //Finally draw your graphics for the node label in each target + if (target instanceof G2DTarget) { + Graphics2D g = ((G2DTarget) target).getGraphics(); + + g.setColor(Color.RED); + g.fillOval((int) node.x(), (int) -node.y(), 5, 5);//Note that y axis is inverse for node coordinates + } else if (target instanceof PDFTarget) { + } else if (target instanceof SVGTarget) { + } + } + + @Override + public PreviewProperty[] getProperties() { + return new PreviewProperty[0]; + } + + @Override + public boolean isRendererForitem(Item item, PreviewProperties properties) { + return item instanceof LabelItem; + } + + @Override + public boolean needsItemBuilder(ItemBuilder itemBuilder, PreviewProperties properties) { + return itemBuilder instanceof ItemBuilderTemplate; + } + + @Override + public boolean needsPreviewMouseListener(PreviewMouseListener pl) { + return pl instanceof MouseListenerTemplate; + } + + public CanvasSize getCanvasSize(Item item, PreviewProperties properties) { + return new CanvasSize(); + } +} diff --git a/src/main/java/fucoin/wallet/AbstractWallet.java b/src/main/java/fucoin/wallet/AbstractWallet.java index 882c2a547b9060a576589281eaaea8c9d3e078ba..210b67e80cc706159f9db444d1cddd75ba2ee0df 100644 --- a/src/main/java/fucoin/wallet/AbstractWallet.java +++ b/src/main/java/fucoin/wallet/AbstractWallet.java @@ -6,6 +6,8 @@ import fucoin.gui.TransactionLogger; import scala.concurrent.Future; import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; /** * @@ -22,6 +24,8 @@ public abstract class AbstractWallet extends AbstractNode implements Serializabl */ protected final String name; + protected final List<ActorRef> overlayNeighbours = new ArrayList<>(); + /** * Init. a wallet with a name. * @@ -114,4 +118,8 @@ public abstract class AbstractWallet extends AbstractNode implements Serializabl * @param observer */ public abstract void send(String address, int amount, ActorRef observer); + + public void addOverlayNeighbour(ActorRef wallet) { + overlayNeighbours.add(wallet); + } } diff --git a/src/main/resources/topology.gexf b/src/main/resources/topology.gexf new file mode 100644 index 0000000000000000000000000000000000000000..45d86d94fb4f0fb2b7ffeccca6292f6313fe5414 --- /dev/null +++ b/src/main/resources/topology.gexf @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<gexf xmlns="http://www.gexf.net/1.3" version="1.3" xmlns:viz="http://www.gexf.net/1.3/viz" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.gexf.net/1.3 http://www.gexf.net/1.3/gexf.xsd"> + <meta lastmodifieddate="2016-07-08"> + <creator>Gephi 0.9</creator> + <description></description> + </meta> + <graph defaultedgetype="directed" mode="static"> + <nodes> + <node id="0" label="Node0"> + <viz:size value="10.0"></viz:size> + <viz:position x="-294.57407" y="300.54153"></viz:position> + <viz:color r="153" g="153" b="153"></viz:color> + </node> + <node id="1" label="Node1"> + <viz:size value="10.0"></viz:size> + <viz:position x="342.42365" y="-58.31403"></viz:position> + <viz:color r="153" g="153" b="153"></viz:color> + </node> + <node id="2" label="Node2"> + <viz:size value="10.0"></viz:size> + <viz:position x="-482.98184" y="-252.69414"></viz:position> + <viz:color r="153" g="153" b="153"></viz:color> + </node> + <node id="3" label="Node3"> + <viz:size value="10.0"></viz:size> + <viz:position x="183.92188" y="291.57016"></viz:position> + <viz:color r="153" g="153" b="153"></viz:color> + </node> + </nodes> + <edges> + <edge id="0" source="2" target="1"></edge> + <edge id="1" source="1" target="3"></edge> + <edge id="2" source="3" target="0"></edge> + <edge id="3" source="0" target="2"></edge> + </edges> + </graph> +</gexf> diff --git a/src/main/resources/topology2.gexf b/src/main/resources/topology2.gexf new file mode 100644 index 0000000000000000000000000000000000000000..f7f453b0be6f37660060bb1bb27ab0241f7de0b6 --- /dev/null +++ b/src/main/resources/topology2.gexf @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<gexf xmlns="http://www.gexf.net/1.3" version="1.3" xmlns:viz="http://www.gexf.net/1.3/viz" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.gexf.net/1.3 http://www.gexf.net/1.3/gexf.xsd"> + <meta lastmodifieddate="2016-07-08"> + <creator>Gephi 0.9</creator> + <description></description> + </meta> + <graph mode="static"> + <nodes> + <node id="0" label="Node0"> + <viz:size value="10.0"></viz:size> + <viz:position x="-294.57407" y="300.54153"></viz:position> + <viz:color r="153" g="153" b="153"></viz:color> + </node> + <node id="1" label="Node1"> + <viz:size value="10.0"></viz:size> + <viz:position x="342.42365" y="-58.31403"></viz:position> + <viz:color r="153" g="153" b="153"></viz:color> + </node> + <node id="2" label="Node2"> + <viz:size value="10.0"></viz:size> + <viz:position x="-482.98184" y="-252.69414"></viz:position> + <viz:color r="153" g="153" b="153"></viz:color> + </node> + <node id="3" label="Node3"> + <viz:size value="10.0"></viz:size> + <viz:position x="183.92188" y="291.57016"></viz:position> + <viz:color r="153" g="153" b="153"></viz:color> + </node> + </nodes> + <edges> + <edge id="0" source="2" target="1" type="directed"></edge> + <edge id="1" source="1" target="3" type="directed"></edge> + <edge id="2" source="3" target="0" type="directed"></edge> + <edge id="4" source="2" target="0" type="undirected"></edge> + </edges> + </graph> +</gexf>