package net.sf.egonet.web.page; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Paint; import java.awt.Polygon; import java.awt.Shape; import java.awt.Stroke; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.TreeMap; import org.apache.commons.collections15.Transformer; import org.apache.wicket.markup.html.link.Link; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import net.sf.egonet.model.Alter; import net.sf.egonet.model.Answer; import net.sf.egonet.model.Expression; import net.sf.egonet.model.Interview; import net.sf.egonet.model.Question; import net.sf.egonet.model.QuestionOption; import net.sf.egonet.model.Question.QuestionType; import net.sf.egonet.network.NetworkService; import net.sf.egonet.persistence.Analysis; import net.sf.egonet.persistence.Expressions; import net.sf.egonet.persistence.Interviews; import net.sf.egonet.persistence.Options; import net.sf.egonet.persistence.Questions; import net.sf.egonet.web.component.NetworkImage; import net.sf.egonet.web.panel.MapEditorPanel; import net.sf.egonet.web.panel.PanelContainer; import net.sf.egonet.web.panel.SingleSelectionPanel; import net.sf.functionalj.tuple.PairUni; import net.sf.functionalj.tuple.TripleUni; public class NetworkVisualizationPage extends EgonetPage { private NetworkImage<Alter> networkImage; private PanelContainer primaryPanel; private PanelContainer secondaryPanel; private Expressions.EvaluationContext context; final ArrayList<Color> colors = Lists.newArrayList(Color.RED,Color.ORANGE, Color.GREEN,Color.BLUE,Color.CYAN, Color.MAGENTA,Color.WHITE,Color.BLACK); final ArrayList<String> colorNames = Lists.newArrayList("Red","Orange", "Green","Blue","Cyan", "Magenta","White","Black"); private String showColor(Color color) { if(color == null) { return " "; } for(int i = 0; i < colors.size(); i++) { if(color.equals(colors.get(i))) { return colorNames.get(i); } } return "Unknown"; } private Interview interview; private Expression connectionReason; private NetworkImage<Alter> buildNetworkImage() { return new NetworkImage<Alter>("networkImage", Analysis.getNetworkForInterview(interview, this.connectionReason)); } public NetworkVisualizationPage(final Interview interview, Expression connectionReason) { super(Interviews.getEgoNameForInterview(interview.getId())); this.interview = interview; this.connectionReason = connectionReason; context = Expressions.getContext(interview); networkImage = buildNetworkImage(); add(networkImage); primaryPanel = new PanelContainer("primaryPanel"); add(primaryPanel); secondaryPanel = new PanelContainer("secondaryPanel"); add(secondaryPanel); add(buildNetworkLayoutLink()); add(buildNetworkBackgroundLink()); add(buildNodeLabelLink()); add(buildNodeColorLink()); add(buildNodeSizeLink()); add(buildNodeShapeLink()); add(buildEdgeColorLink()); add(buildEdgeSizeLink()); add(buildAdjacencyReasonLink()); } // XXX: Section network layout private NetworkService.LayoutOption layoutOption = null; private Link buildNetworkLayoutLink() { return new Link("layoutLink") { public void onClick() { ArrayList<NetworkService.LayoutOption> options = Lists.newArrayList(NetworkService.LayoutOption.values()); primaryPanel.changePanel( new SingleSelectionPanel<NetworkService.LayoutOption>("panel", "Layout",options) { public void action(NetworkService.LayoutOption option) { layoutOption = option; networkImage.setLayoutOption(option); networkImage.refresh(); } }); secondaryPanel.removePanel(); } }; } // XXX: Section network background private Color backgroundColor = Color.WHITE; private Link buildNetworkBackgroundLink() { return new Link("backgroundLink") { public void onClick() { primaryPanel.changePanel( new SingleSelectionPanel<Color>("panel", "Background",colors) { public void action(Color option) { backgroundColor = option; networkImage.setBackground(backgroundColor); networkImage.refresh(); } public String show(Color color) { return showColor(color); } }); secondaryPanel.removePanel(); } }; } // XXX: Common helpers private ArrayList<Object> noneAndSelectionQuestionsOfQuestionType(QuestionType questionType) { ArrayList<Object> results = Lists.newArrayList(); results.add("None"); List<Question> questions = Questions.getQuestionsForStudy(interview.getStudyId(), questionType); for(Question question : questions) { if(question.getAnswerType().equals(Answer.AnswerType.SELECTION) || question.getAnswerType().equals(Answer.AnswerType.MULTIPLE_SELECTION)) { results.add(question); } } return results; } private ArrayList<Object> alterSelectionQuestionsAndNone() { return noneAndSelectionQuestionsOfQuestionType(QuestionType.ALTER); } private ArrayList<Object> alterPairSelectionQuestionsAndNone() { return noneAndSelectionQuestionsOfQuestionType(QuestionType.ALTER_PAIR); } // XXX: Section node label private AlterLabeller alterLabeller = null; private Link buildNodeLabelLink() { return new Link("nodeLabelLink") { public void onClick() { ArrayList<AlterLabeller> options = Lists.newArrayList(new AlterNoneLabeller(), new AlterLabeller()); List<Question> questions = Questions.getQuestionsForStudy(interview.getStudyId(), QuestionType.ALTER); for(Question question : questions) { options.add(new AlterQuestionLabeller(question)); } primaryPanel.changePanel( new SingleSelectionPanel<AlterLabeller>("panel", "Alter Label",options) { public void action(AlterLabeller option) { alterLabeller = option; networkImage.setNodeLabeller(alterLabeller); networkImage.refresh(); } }); secondaryPanel.removePanel(); } }; } public class AlterLabeller implements Transformer<Alter,String>, Serializable { public String transform(Alter alter) { return alter.getName(); } public String toString() { return "Name"; } } public class AlterNoneLabeller extends AlterLabeller { public String transform(Alter alter) { return ""; } public String toString() { return "None"; } } public class AlterQuestionLabeller extends AlterLabeller { private Question question; private HashMap<Long,QuestionOption> idToOption; public AlterQuestionLabeller(Question question) { this.question = question; idToOption = Maps.newHashMap(); for(QuestionOption option : Options.getOptionsForQuestion(question.getId())) { idToOption.put(option.getId(), option); } } public String transform(Alter alter) { Answer answer = context.qidAidToAlterAnswer.get( new PairUni<Long>(question.getId(),alter.getId())); if(answer == null || answer.getValue() == null || answer.getValue().isEmpty()) { return ""; } else if(question.getAnswerType().equals(Answer.AnswerType.TEXTUAL) || question.getAnswerType().equals(Answer.AnswerType.TEXTUAL_PP) || question.getAnswerType().equals(Answer.AnswerType.NUMERICAL)) { return answer.getValue(); } else { String result = ""; for(String optionId : answer.getValue().split(",")) { try { result += (result.isEmpty() ? "" : ", ")+ idToOption.get(Long.parseLong(optionId)).getName(); } catch(Exception ex) { } } return result; } } public String toString() { return question.getTitle(); } } public class AlterExpressionLabeller extends AlterLabeller { public String transform(Alter alter) { return alter.getName(); } } // XXX: Section node color private Question nodeColorQuestion; private TreeMap<Question,TreeMap<QuestionOption,Color>> nodeColorSelectionQuestionDetails; private Link buildNodeColorLink() { nodeColorSelectionQuestionDetails = Maps.newTreeMap(); return new Link("nodeColorLink") { public void onClick() { nodeColorChangePrimary(); nodeColorChangeSecondary(); } }; } private void nodeColorChangePrimary() { primaryPanel.changePanel( new SingleSelectionPanel<Object>("panel","Color node based on",alterSelectionQuestionsAndNone()) { public String show(Object object) { return object instanceof Question ? ((Question) object).getTitle() : object.toString(); } public void action(Object option) { if(option.equals("None")) { nodeColorQuestion = null; } else if(option instanceof Question) { nodeColorQuestion = (Question) option; } else { throw new RuntimeException("Unrecognized colorizing option: "+option); } nodeColorChangeSecondary(); nodeColorUpdate(true); } }); } private void nodeColorChangeSecondary() { if(nodeColorQuestion == null) { secondaryPanel.removePanel(); } else { if(!nodeColorSelectionQuestionDetails.containsKey(nodeColorQuestion)) { nodeColorSelectionQuestionDetails.put(nodeColorQuestion, new TreeMap<QuestionOption,Color>()); } secondaryPanel.changePanel( new MapEditorPanel<QuestionOption,Color>("panel", "Color for each "+nodeColorQuestion.getTitle()+" option", "Color for $$", nodeColorSelectionQuestionDetails.get(nodeColorQuestion), Options.getOptionsForQuestion(nodeColorQuestion.getId()), colors) { protected String showValue(Color color) { return showColor(color); } protected void mapChanged() { nodeColorUpdate(true); } }); } } private void nodeColorUpdate(Boolean shouldRefresh) { if(nodeColorQuestion == null) { networkImage.setNodeColorizer(new AlterColorizer()); } else { networkImage.setNodeColorizer( new AlterQuestionColorizer( nodeColorQuestion, nodeColorSelectionQuestionDetails .get(nodeColorQuestion))); } if(shouldRefresh) { networkImage.refresh(); } } public class AlterColorizer implements Transformer<Alter,Paint>, Serializable { public Paint transform(Alter alter) { return Color.WHITE; } public String toString() { return "None"; } } public class AlterQuestionColorizer extends AlterColorizer { private Question question; private TreeMap<QuestionOption,Color> configuration; public AlterQuestionColorizer(Question question, TreeMap<QuestionOption,Color> configuration) { this.question = question; this.configuration = configuration; } public Paint transform(Alter alter) { String answerValue = context.qidAidToAlterAnswer.get( new PairUni<Long>(question.getId(),alter.getId())) .getValue(); if(question.getAnswerType().equals(Answer.AnswerType.SELECTION) || question.getAnswerType().equals(Answer.AnswerType.MULTIPLE_SELECTION)) { for(String optionIdString : Lists.newArrayList(answerValue.split(","))) { Color color = configuration.get(context.idToOption.get(Long.parseLong(optionIdString))); if(color != null) { return color; } } } return Color.WHITE; } public String toString() { return question.getTitle(); } public ArrayList<QuestionOption> getConfigKeys() { return Options.getOptionsForQuestion(question.getId()); } } // XXX: Section node size final ArrayList<Integer> sizes = Lists.newArrayList(10,20,30,40,50); final ArrayList<Integer> sizeNames = Lists.newArrayList(1,2,3,4,5); private String showSizeName(Integer sizeName) { return sizeName == null ? " " : sizeName+""; } private Integer sizeOfName(Integer sizeName) { return sizes.get(sizeName == null ? 0 : (sizeName-1)); } private Question nodeSizeQuestion; private TreeMap<Question,TreeMap<QuestionOption,Integer>> nodeSizeSelectionQuestionDetails; private Link buildNodeSizeLink() { nodeSizeSelectionQuestionDetails = Maps.newTreeMap(); return new Link("nodeSizeLink") { public void onClick() { nodeSizeChangePrimary(); nodeSizeChangeSecondary(); } }; } private void nodeSizeChangePrimary() { primaryPanel.changePanel( new SingleSelectionPanel<Object>("panel","Size node based on", alterSelectionQuestionsAndNone()) { public String show(Object object) { return object instanceof Question ? ((Question) object).getTitle() : object.toString(); } public void action(Object option) { if(option.equals("None")) { nodeSizeQuestion = null; } else if(option instanceof Question) { nodeSizeQuestion = (Question) option; } else { throw new RuntimeException("Unrecognized sizing option: "+option); } nodeSizeChangeSecondary(); nodeShapeAndSizeUpdate(true); } }); } private void nodeSizeChangeSecondary() { if(nodeSizeQuestion == null) { secondaryPanel.removePanel(); } else { if(!nodeSizeSelectionQuestionDetails.containsKey(nodeSizeQuestion)) { nodeSizeSelectionQuestionDetails.put(nodeSizeQuestion, new TreeMap<QuestionOption,Integer>()); } secondaryPanel.changePanel( new MapEditorPanel<QuestionOption,Integer>("panel", "Size for each "+nodeSizeQuestion.getTitle()+" option", "Size for $$", nodeSizeSelectionQuestionDetails.get(nodeSizeQuestion), Options.getOptionsForQuestion(nodeSizeQuestion.getId()), sizeNames) { protected String showValue(Integer sizeName) { return showSizeName(sizeName); } protected void mapChanged() { nodeShapeAndSizeUpdate(true); } }); } } // XXX: Section node shape final ArrayList<Integer> shapeSides = Lists.newArrayList(1,3,4,5,6); final ArrayList<String> shapeNames = Lists.newArrayList("Circle","Triangle","Square","Pentagon","Hexagon"); private String showShapeName(Integer sides) { if(sides != null) { for(Integer i = 0; i < shapeSides.size(); i++) { if(shapeSides.get(i).equals(sides)) { return shapeNames.get(i); } } } return sides == null || sides < 3 ? "Circle" : (sides+"-gon"); } private Question nodeShapeQuestion; private TreeMap<Question,TreeMap<QuestionOption,Integer>> nodeShapeSelectionQuestionDetails; private Link buildNodeShapeLink() { nodeShapeSelectionQuestionDetails = Maps.newTreeMap(); return new Link("nodeShapeLink") { public void onClick() { nodeShapeChangePrimary(); nodeShapeChangeSecondary(); } }; } private void nodeShapeChangePrimary() { primaryPanel.changePanel( new SingleSelectionPanel<Object>("panel","Shape node based on", alterSelectionQuestionsAndNone()) { public String show(Object object) { return object instanceof Question ? ((Question) object).getTitle() : object.toString(); } public void action(Object option) { if(option.equals("None")) { nodeShapeQuestion = null; } else if(option instanceof Question) { nodeShapeQuestion = (Question) option; } else { throw new RuntimeException("Unrecognized shaping option: "+option); } nodeShapeChangeSecondary(); nodeShapeAndSizeUpdate(true); } }); } private void nodeShapeChangeSecondary() { if(nodeShapeQuestion == null) { secondaryPanel.removePanel(); } else { if(!nodeShapeSelectionQuestionDetails.containsKey(nodeShapeQuestion)) { nodeShapeSelectionQuestionDetails.put(nodeShapeQuestion, new TreeMap<QuestionOption,Integer>()); } secondaryPanel.changePanel( new MapEditorPanel<QuestionOption,Integer>("panel", "Shape for each "+nodeShapeQuestion.getTitle()+" option", "Shape for $$", nodeShapeSelectionQuestionDetails.get(nodeShapeQuestion), Options.getOptionsForQuestion(nodeShapeQuestion.getId()), shapeSides) { protected String showValue(Integer sides) { return showShapeName(sides); } protected void mapChanged() { nodeShapeAndSizeUpdate(true); } }); } } private void nodeShapeAndSizeUpdate(Boolean shouldRefresh) { networkImage.setNodeShaper(new Transformer<Alter, Shape>() { public Shape transform(Alter alter) { return new RegularPolygon( nodeSides(alter), nodeSize(alter)); } private Integer nodeSides(Alter alter) { Integer sides = null; if(nodeShapeQuestion != null) { String answerValue = context.qidAidToAlterAnswer.get( new PairUni<Long>(nodeShapeQuestion.getId(),alter.getId())) .getValue(); if(nodeShapeQuestion.getAnswerType().equals(Answer.AnswerType.SELECTION) || nodeShapeQuestion.getAnswerType().equals(Answer.AnswerType.MULTIPLE_SELECTION)) { for(String optionIdString : Lists.newArrayList(answerValue.split(","))) { sides = nodeShapeSelectionQuestionDetails .get(nodeShapeQuestion) .get(context.idToOption.get(Long.parseLong(optionIdString))); if(sides != null) { return sides < 3 ? 20 : sides; } } } } return 20; } private Integer nodeSize(Alter alter) { if(nodeSizeQuestion != null) { String answerValue = context.qidAidToAlterAnswer.get( new PairUni<Long>(nodeSizeQuestion.getId(),alter.getId())) .getValue(); if(nodeSizeQuestion.getAnswerType().equals(Answer.AnswerType.SELECTION) || nodeSizeQuestion.getAnswerType().equals(Answer.AnswerType.MULTIPLE_SELECTION)) { for(String optionIdString : Lists.newArrayList(answerValue.split(","))) { Integer sizeName = nodeSizeSelectionQuestionDetails .get(nodeSizeQuestion) .get(context.idToOption.get(Long.parseLong(optionIdString))); if(sizeName != null) { return sizeOfName(sizeName); } } } } return sizeOfName(null); } }); if(shouldRefresh) { networkImage.refresh(); } } private static class RegularPolygon extends Polygon { public RegularPolygon(Integer sides, Integer size) { super(xcoords(sides,size),ycoords(sides,size),sides); } private static int[] xcoords(Integer sides, Integer size) { int[] result = new int[sides]; for(Integer i = 0; i < sides; i++) { result[i] = (int) (Math.sin(2*(i+0.5)*Math.PI/sides)*size); } return result; } private static int[] ycoords(Integer sides, Integer size) { int[] result = new int[sides]; for(Integer i = 0; i < sides; i++) { result[i] = (int) (Math.cos(2*(i+0.5)*Math.PI/sides)*size); } return result; } } // XXX: Section edge color private Question edgeColorQuestion; private TreeMap<Question,TreeMap<QuestionOption,Color>> edgeColorSelectionQuestionDetails; private Link buildEdgeColorLink() { edgeColorSelectionQuestionDetails = Maps.newTreeMap(); return new Link("edgeColorLink") { public void onClick() { edgeColorChangePrimary(); edgeColorChangeSecondary(); } }; } private void edgeColorChangePrimary() { primaryPanel.changePanel( new SingleSelectionPanel<Object>("panel","Color edge based on",alterPairSelectionQuestionsAndNone()) { public String show(Object object) { return object instanceof Question ? ((Question) object).getTitle() : object.toString(); } public void action(Object option) { if(option.equals("None")) { edgeColorQuestion = null; } else if(option instanceof Question) { edgeColorQuestion = (Question) option; } else { throw new RuntimeException("Unrecognized edge colorizing option: "+option); } edgeColorChangeSecondary(); edgeColorUpdate(true); networkImage.refresh(); } }); } private void edgeColorChangeSecondary() { if(edgeColorQuestion == null) { secondaryPanel.removePanel(); } else { if(!edgeColorSelectionQuestionDetails.containsKey(edgeColorQuestion)) { edgeColorSelectionQuestionDetails.put(edgeColorQuestion, new TreeMap<QuestionOption,Color>()); } secondaryPanel.changePanel( new MapEditorPanel<QuestionOption,Color>("panel", "Color for each "+edgeColorQuestion.getTitle()+" option", "Color for $$", edgeColorSelectionQuestionDetails.get(edgeColorQuestion), Options.getOptionsForQuestion(edgeColorQuestion.getId()), colors) { protected String showValue(Color color) { return showColor(color); } protected void mapChanged() { edgeColorUpdate(true); } }); } } private void edgeColorUpdate(Boolean shouldRefresh) { if(edgeColorQuestion == null) { networkImage.setEdgeColorizer(new EdgeColorizer()); } else { networkImage.setEdgeColorizer( new AlterPairQuestionColorizer( edgeColorQuestion, edgeColorSelectionQuestionDetails .get(edgeColorQuestion))); } if(shouldRefresh) { networkImage.refresh(); } } public class EdgeColorizer implements Transformer<PairUni<Alter>,Paint>, Serializable { public Paint transform(PairUni<Alter> alterPair) { return Color.BLACK; } public String toString() { return "None"; } } public class AlterPairQuestionColorizer extends EdgeColorizer { private Question question; private TreeMap<QuestionOption,Color> configuration; public AlterPairQuestionColorizer(Question question, TreeMap<QuestionOption,Color> configuration) { this.question = question; this.configuration = configuration; } public Paint transform(PairUni<Alter> alterPair) { String answerValue = context.qidA1idA2idToAlterPairAnswer.get( new TripleUni<Long>(question.getId(), alterPair.getFirst().getId(), alterPair.getSecond().getId())) .getValue(); if(question.getAnswerType().equals(Answer.AnswerType.SELECTION) || question.getAnswerType().equals(Answer.AnswerType.MULTIPLE_SELECTION)) { for(String optionIdString : Lists.newArrayList(answerValue.split(","))) { Color color = configuration.get(context.idToOption.get(Long.parseLong(optionIdString))); if(color != null) { return color; } } } return Color.BLACK; } public String toString() { return question.getTitle(); } public ArrayList<QuestionOption> getConfigKeys() { return Options.getOptionsForQuestion(question.getId()); } } // XXX: Section edge size private Question edgeSizeQuestion; private TreeMap<Question,TreeMap<QuestionOption,Integer>> edgeSizeSelectionQuestionDetails; private Link buildEdgeSizeLink() { edgeSizeSelectionQuestionDetails = Maps.newTreeMap(); return new Link("edgeSizeLink") { public void onClick() { edgeSizeChangePrimary(); edgeSizeChangeSecondary(); } }; } private void edgeSizeChangePrimary() { primaryPanel.changePanel( new SingleSelectionPanel<Object>("panel","Size edge based on",alterPairSelectionQuestionsAndNone()) { public String show(Object object) { return object instanceof Question ? ((Question) object).getTitle() : object.toString(); } public void action(Object option) { if(option.equals("None")) { edgeSizeQuestion = null; } else if(option instanceof Question) { edgeSizeQuestion = (Question) option; } else { throw new RuntimeException("Unrecognized edge Sizing option: "+option); } edgeSizeChangeSecondary(); edgeSizeUpdate(true); } }); } private void edgeSizeChangeSecondary() { if(edgeSizeQuestion == null) { secondaryPanel.removePanel(); } else { if(!edgeSizeSelectionQuestionDetails.containsKey(edgeSizeQuestion)) { edgeSizeSelectionQuestionDetails.put(edgeSizeQuestion, new TreeMap<QuestionOption,Integer>()); } secondaryPanel.changePanel( new MapEditorPanel<QuestionOption,Integer>("panel", "Size for each "+edgeSizeQuestion.getTitle()+" option", "Size for $$", edgeSizeSelectionQuestionDetails.get(edgeSizeQuestion), Options.getOptionsForQuestion(edgeSizeQuestion.getId()), sizeNames) { protected String showValue(Integer size) { return size == null ? "" : size+""; } protected void mapChanged() { edgeSizeUpdate(true); } }); } } private void edgeSizeUpdate(Boolean shouldRefresh) { if(edgeSizeQuestion == null) { networkImage.setEdgeSizer(new EdgeSizer()); } else { networkImage.setEdgeSizer( new AlterPairQuestionSizer( edgeSizeQuestion, edgeSizeSelectionQuestionDetails .get(edgeSizeQuestion))); } if(shouldRefresh) { networkImage.refresh(); } } // TODO: might need to use alters to decide if there should even be an edge private Stroke strokeForAlterPairAndSize(PairUni<Alter> alters, Integer size) { return new BasicStroke(1.3f * size, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f); } public class EdgeSizer implements Transformer<PairUni<Alter>,Stroke>, Serializable { public Stroke transform(PairUni<Alter> alterPair) { return strokeForAlterPairAndSize(alterPair,1); } public String toString() { return "None"; } } public class AlterPairQuestionSizer extends EdgeSizer { private Question question; private TreeMap<QuestionOption,Integer> configuration; public AlterPairQuestionSizer(Question question, TreeMap<QuestionOption,Integer> configuration) { this.question = question; this.configuration = configuration; } public Stroke transform(PairUni<Alter> alterPair) { String answerValue = context.qidA1idA2idToAlterPairAnswer.get( new TripleUni<Long>(question.getId(), alterPair.getFirst().getId(), alterPair.getSecond().getId())) .getValue(); Integer size = 1; if(question.getAnswerType().equals(Answer.AnswerType.SELECTION) || question.getAnswerType().equals(Answer.AnswerType.MULTIPLE_SELECTION)) { for(String optionIdString : Lists.newArrayList(answerValue.split(","))) { Integer newSize = configuration.get(context.idToOption.get(Long.parseLong(optionIdString))); if(newSize != null && newSize > size) { size = newSize; } } } return strokeForAlterPairAndSize(alterPair,size); } public String toString() { return question.getTitle(); } public ArrayList<QuestionOption> getConfigKeys() { return Options.getOptionsForQuestion(question.getId()); } } // XXX: Section adjacency reason private Link buildAdjacencyReasonLink() { return new Link("adjacencyReasonLink") { public void onClick() { ArrayList<Expression> options = Lists.newArrayList( Expressions.forStudy(interview.getStudyId())); primaryPanel.changePanel( new SingleSelectionPanel<Expression>("panel", "Adjacency Reason",options) { public void action(Expression option) { connectionReason = option; NetworkImage<Alter> newNetworkImage = buildNetworkImage(); networkImage.replaceWith(newNetworkImage); networkImage = newNetworkImage; if(layoutOption != null) { networkImage.setLayoutOption(layoutOption); } if(backgroundColor != null) { networkImage.setBackground(backgroundColor); } if(alterLabeller != null) { networkImage.setNodeLabeller(alterLabeller); } nodeColorUpdate(false); nodeShapeAndSizeUpdate(false); edgeColorUpdate(false); edgeSizeUpdate(false); networkImage.refresh(); } }); secondaryPanel.removePanel(); } }; } }