package beast.app.beauti; import java.lang.reflect.Method; import java.util.List; import beast.core.BEASTInterface; import beast.core.BEASTObject; import beast.core.Description; import beast.core.Input; import beast.core.Input.Validate; import beast.core.MCMC; import beast.core.Operator; @Description("Specifies which part of the template get connected to the main network") public class BeautiConnector extends BEASTObject { final public Input<String> methodnput = new Input<>("method", "name of static method that should be called with BeautiDoc as " + "argument. For example beast.app.beauti.SiteModelInputEditor.custmoConnector"); final public Input<String> sourceIDInput = new Input<>("srcID", "ID of the beastObject to be connected", Validate.XOR, methodnput); final public Input<String> targetIDInput = new Input<>("targetID", "ID of beastObject to connect to", Validate.XOR, methodnput); final public Input<String> inputNameInput = new Input<>("inputName", "name of the input of the beastObject to connect to", Validate.XOR, methodnput); final public Input<String> tipText = new Input<>("value", "associate some tip text with the srcID beastObject, useful for displaying prior and operator specific information"); final public Input<String> conditionInput = new Input<>("if", "condition under which this connector should be executed." + "These should be of the form " + "inposterior(id) or id/input=value, e.g. inposterior(kappa), kappa/estimate=true. " + "inlikelihood(id) to check there is a beastObject with suplied id that is predecessor of likelihood. " + "nooperator(id) to check there is no operator with suplied id. " + "isInitialising to execute only when subtemplate is first instantiated. " + "For partition specific ids, use $(n), e.g. e.g. kappa.$(n)/estimate=true. " + "For multiple conditions, separate by 'and', e.g. inposterior(kappa.$(n)) and kappa.$(n)/estimate=true"); // public enum ConnectCondition {always, ifunlinked}; // public Input<ConnectCondition> connectCondition = new Input<>("connect","condition when to connect. Default is 'always'. " + // "With ifunlinked, the connector is only activated if the link does not already exists. " + // "Possible values: " + ConnectCondition.values(), // ConnectCondition.always, ConnectCondition.values()); enum Operation {EQUALS, NOT_EQUALS, IS_IN_POSTERIOR, IS_IN_LIKELIHOOD, IS_NOT_AN_OPERTOR, AT_INITIALISATION_ONLY} // final static String IS_IN_POSTERIOR = "x"; // final static String AT_INITIALISATION_ONLY = "y"; String sourceID; String targetID; String targetInput; String[] conditionIDs; String[] conditionInputs; Operation[] conditionOperations; String[] conditionValues; boolean isRegularConnector = true; Method method = null; public BeautiConnector() {} public BeautiConnector(String sourceID, String targetID, String inputName, String condition) { initByName("srcID", sourceID, "targetID", targetID, "inputName", inputName, "if", condition); } @Override public void initAndValidate() { sourceID = sourceIDInput.get(); targetID = targetIDInput.get(); targetInput = inputNameInput.get(); if (conditionInput.get() != null) { String[] conditions = conditionInput.get().split("\\s+and\\s+"); conditionIDs = new String[conditions.length]; conditionInputs = new String[conditions.length]; conditionValues = new String[conditions.length]; conditionOperations = new Operation[conditions.length]; for (int i = 0; i < conditions.length; i++) { String s = conditions[i]; if (s.startsWith("inposterior(")) { conditionIDs[i] = s.substring(s.indexOf("(") + 1, s.lastIndexOf(")")); conditionInputs[i] = null; conditionOperations[i] = Operation.IS_IN_POSTERIOR; conditionValues[i] = null; } else if (s.startsWith("inlikelihood(")) { conditionIDs[i] = s.substring(s.indexOf("(") + 1, s.lastIndexOf(")")); conditionInputs[i] = null; conditionOperations[i] = Operation.IS_IN_LIKELIHOOD; conditionValues[i] = null; } else if (s.startsWith("nooperator")) { conditionIDs[i] = s.substring(s.indexOf("(") + 1, s.lastIndexOf(")")); conditionOperations[i] = Operation.IS_NOT_AN_OPERTOR; conditionInputs[i] = null; conditionValues[i] = null; } else if (s.startsWith("isInitializing")) { conditionIDs[i] = null; conditionOperations[i] = Operation.AT_INITIALISATION_ONLY; conditionInputs[i] = null; conditionValues[i] = null; } else { conditionIDs[i] = s.substring(0, s.indexOf("/")); conditionInputs[i] = s.substring(s.indexOf("/") + 1, s.indexOf("=")); conditionValues[i] = s.substring(s.indexOf("=") + 1); conditionOperations[i] = Operation.EQUALS; if (conditionInputs[i].endsWith("!")) { conditionInputs[i] = conditionInputs[i].substring(0, conditionInputs[i].length() - 1); conditionOperations[i] = Operation.NOT_EQUALS; } } } } else { conditionIDs = new String[0]; conditionInputs = new String[0]; conditionOperations = new Operation[0]; conditionValues = new String[0]; } if (methodnput.get() != null) { String fullMethod = methodnput.get(); String className = fullMethod.substring(0, fullMethod.lastIndexOf('.')); String methodName = fullMethod.substring(fullMethod.lastIndexOf('.') + 1); Class<?> class_; try { class_ = Class.forName(className); method = class_.getMethod(methodName, BeautiDoc.class); } catch (ClassNotFoundException | NoSuchMethodException | SecurityException e) { throw new IllegalArgumentException(e.getMessage()); } isRegularConnector = false; } } public boolean atInitialisationOnly() { if (conditionOperations.length > 0) { return conditionOperations[0].equals(Operation.AT_INITIALISATION_ONLY); } else { return false; } } /** * check that conditions in the 'if' input are met * */ public boolean isActivated(PartitionContext partitionContext, List<BEASTInterface> posteriorPredecessors, List<BEASTInterface> likelihoodPredecessors, BeautiDoc doc) { if (atInitialisationOnly()) { return false; } if (methodnput.get() != null) { // if (method != null) { try { String fullMethod = methodnput.get(); String className = fullMethod.substring(0, fullMethod.lastIndexOf('.')); String methodName = fullMethod.substring(fullMethod.lastIndexOf('.') + 1); Class<?> class_ = Class.forName(className); method = class_.getMethod(methodName, BeautiDoc.class); method.invoke(null, doc); } catch (Exception e) { // ignore } } boolean isActive = true; for (int i = 0; i < conditionIDs.length; i++) { //String id = conditionIDs[i].replaceAll("\\$\\(n\\)", partition); String id = BeautiDoc.translatePartitionNames(conditionIDs[i], partitionContext); BEASTInterface beastObject = doc.pluginmap.get(id); if (beastObject == null) { if (conditionOperations[i] != Operation.IS_NOT_AN_OPERTOR) { return false; } //System.err.println("isActivated::no beastObject found"); } //System.err.println("isActivated::found " + id); try { switch (conditionOperations[i]) { case IS_IN_POSTERIOR: if (!posteriorPredecessors.contains(beastObject)) { //System.err.println(posteriorPredecessors); //System.err.println("isActivated::is not in posterior, return false"); return false; } break; case IS_IN_LIKELIHOOD: if (!likelihoodPredecessors.contains(beastObject)) { //System.err.println(posteriorPredecessors); //System.err.println("isActivated::is not in posterior, return false"); return false; } break; //System.err.println("isActivated::is in posterior"); case IS_NOT_AN_OPERTOR: List<Operator> operators = ((MCMC) doc.mcmc.get()).operatorsInput.get(); if (operators.contains(beastObject)) { return false; } break; case EQUALS: final Input<?> input = beastObject.getInput(conditionInputs[i]); //System.err.println("isActivated::input " + input.get().toString() + " expected " + conditionValues[i]); if (input.get() == null) { if (!conditionValues[i].equals("null")) { return false; } } else if (!input.get().toString().equals(conditionValues[i])) { //System.err.println("isActivated::return false"); return false; } break; case NOT_EQUALS: final Input<?> input2 = beastObject.getInput(conditionInputs[i]); //System.err.println("isActivated::input " + input.get().toString() + " expected " + conditionValues[i]); if (input2.get() == null) { if (conditionValues[i].equals("null")) { return false; } } else if (input2.get().toString().equals(conditionValues[i])) { //System.err.println("isActivated::return false"); return false; } break; default: throw new IllegalArgumentException("Unexpected operation: " + conditionOperations[i]); } } catch (Exception e) { e.printStackTrace(); return false; } } //if (conditionIDs.length > 0) { // System.err.println("isActivated::return true"); //} return isActive; } public String getTipText() { return tipText.get(); } @Override public String toString() { if (methodnput.get() != null) { return "call " + methodnput.get(); } return "@" + sourceID + " -> @" + targetID + "/" + targetInput; } public String toString(PartitionContext context) { if (methodnput.get() != null) { return toString(); } return "@" + BeautiDoc.translatePartitionNames(sourceID, context) + " -> @" + targetID + "/" + BeautiDoc.translatePartitionNames(targetInput, context); } }