/** * create new instance of src object, connecting all inputs from src object Note if input is a * SubstModel, it is duplicated as well. * * @param src object to be copied * @param i index used to extend ID with. * @return copy of src object */ private Object duplicate(BEASTInterface src, int i) { if (src == null) { return null; } BEASTInterface copy; try { copy = src.getClass().newInstance(); copy.setID(src.getID() + "_" + i); } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); throw new RuntimeException( "Programmer error: every object in the model should have a default constructor that is publicly accessible: " + src.getClass().getName()); } for (Input<?> input : src.listInputs()) { if (input.get() != null) { if (input.get() instanceof List) { // handle lists // ((List)copy.getInput(input.getName())).clear(); for (Object o : (List<?>) input.get()) { if (o instanceof BEASTInterface) { // make sure it is not already in the list copy.setInputValue(input.getName(), o); } } } else if (input.get() instanceof SubstitutionModel) { // duplicate subst models BEASTInterface substModel = (BEASTInterface) duplicate((BEASTInterface) input.get(), i); copy.setInputValue(input.getName(), substModel); } else { // it is some other value copy.setInputValue(input.getName(), input.get()); } } } copy.initAndValidate(); return copy; }
/** * produce XML for an input of a beast object, both as attribute/value pairs for primitive inputs * (if isShort=true) and as individual elements (if isShort=false) * * @param input: name of the input * @param beastObject: beast object to produce this input XML for * @param buf: gets XML results are appended * @param isShort: flag to indicate attribute/value format (true) or element format (false) * @throws Exception */ void inputToXML( Input<?> input, Object value, BEASTInterface beastObject, StringBuffer buf, boolean isShort) throws Exception { // if (input.getName().equals("*")) { // this can happen with beast.core.parameter.Map // and * is not a valid XML attribute name // return; // } if (value != null) { if (value instanceof Map) { // distinguish between List, Map, BEASTInterface and primitive input types if (isShort) { @SuppressWarnings("unchecked") Map<String, ?> map = (Map<String, ?>) value; // determine label width int whiteSpaceWidth = 0; List<String> keys = new ArrayList<>(); keys.addAll(map.keySet()); Collections.sort(keys); for (String key : keys) { whiteSpaceWidth = Math.max(whiteSpaceWidth, key.length()); } for (String key : map.keySet()) { // buf.append(" <input name='" + key + "'>"); buf.append("\n " + key); for (int k = key.length(); k < whiteSpaceWidth; k++) { buf.append(' '); } buf.append("=\"" + normalise(map.get(key).toString()) + "\""); } } return; } else if (value instanceof List) { if (!isShort) { int k = 0; List<?> list = (List<?>) value; for (Object o2 : list) { if (o2 instanceof BEASTInterface) { beastObjectToXML((BEASTInterface) o2, buf, input.getName(), false); } else { k++; buf.append(o2.toString()); if (k < list.size()) { buf.append(' '); } } } } return; } else if (value instanceof BEASTInterface) { if (!value.equals(input.defaultValue)) { if (isShort && isDone.contains(value)) { buf.append( " " + input.getName() + "='@" + normalise(((BEASTInterface) value).getID()) + "'"); if (!isInputsDone.containsKey(beastObject)) { isInputsDone.put(beastObject, new HashSet<>()); } isInputsDone.get(beastObject).add(input.getName()); } if (!isShort && (!isInputsDone.containsKey(beastObject) || !isInputsDone.get(beastObject).contains(input.getName()))) { beastObjectToXML((BEASTInterface) value, buf, input.getName(), false); } } return; } else { if (!value.equals(input.defaultValue)) { // primitive type String valueString = value.toString(); if (isShort) { if (valueString.indexOf('\n') < 0) { buf.append(" " + input.getName() + "='" + normalise(value.toString()) + "'"); } } else { if (valueString.indexOf('\n') >= 0) { for (int j = 0; j < indent; j++) { buf.append(" "); } if (input.getName().equals("value")) { buf.append(normalise(value.toString())); } else { buf.append( "<input name='" + input.getName() + "'>" + normalise(value.toString()) + "</input>\n"); } } } } return; } } else { // value=null, no XML to produce return; } } // inputToXML
/** Ensure the class behaves properly, even when inputs are not specified. */ @Override public void initAndValidate() throws Exception { boolean sortNodesAlphabetically = false; if (dataInput.get() != null) { labels = dataInput.get().getTaxaNames(); } else if (m_taxonset.get() != null) { if (labels == null) { labels = m_taxonset.get().asStringList(); } else { // else labels were set by TreeParser c'tor sortNodesAlphabetically = true; } } else { if (isLabelledNewickInput.get()) { if (m_initial.get() != null) { labels = m_initial.get().getTaxonset().asStringList(); } else { labels = new ArrayList<>(); createUnrecognizedTaxa = true; sortNodesAlphabetically = true; } } else { if (m_initial.get() != null) { // try to pick up taxa from initial tree final Tree tree = m_initial.get(); if (tree.m_taxonset.get() != null) { labels = tree.m_taxonset.get().asStringList(); } else { // m_sLabels = null; } } else { // m_sLabels = null; } } // m_bIsLabelledNewick = false; } final String newick = newickInput.get(); if (newick == null || newick.equals("")) { // can happen while initalising Beauti final Node dummy = new Node(); setRoot(dummy); } else { try { setRoot(parseNewick(newickInput.get())); } catch (ParseCancellationException e) { throw new RuntimeException( "TreeParser cannot make sense of the Newick string " + "provided. It gives the following clue:\n" + e.getMessage()); } } super.initAndValidate(); if (sortNodesAlphabetically) { // correct for node ordering: ensure order is alphabetical for (int i = 0; i < getNodeCount() && i < labels.size(); i++) { m_nodes[i].setID(labels.get(i)); } Node[] nodes = new Node[labels.size()]; System.arraycopy(m_nodes, 0, nodes, 0, labels.size()); Arrays.sort(nodes, (o1, o2) -> o1.getID().compareTo(o2.getID())); for (int i = 0; i < labels.size(); i++) { m_nodes[i] = nodes[i]; nodes[i].setNr(i); } } if (m_initial.get() != null) processTraits(m_initial.get().m_traitList.get()); else processTraits(m_traitList.get()); if (timeTraitSet != null) { adjustTreeNodeHeights(root); } else if (adjustTipHeightsInput.get()) { double treeLength = TreeUtils.getTreeLength(this, getRoot()); double extraTreeLength = 0.0; double maxTipHeight = 0.0; // all nodes should be at zero height if no date-trait is available for (int i = 0; i < getLeafNodeCount(); i++) { double height = getNode(i).getHeight(); if (maxTipHeight < height) { maxTipHeight = height; } extraTreeLength += height; getNode(i).setHeight(0); } double scaleFactor = (treeLength + extraTreeLength) / treeLength; final double SCALE_FACTOR_THRESHOLD = 0.001; // if the change in total tree length is more than 0.1% then give the user a warning! if (scaleFactor > 1.0 + SCALE_FACTOR_THRESHOLD) { DecimalFormat format = new DecimalFormat("#.##"); Log.info.println( "WARNING: Adjust tip heights attribute set to 'true' in " + getClass().getSimpleName()); Log.info.println( " has resulted in significant (>" + format.format(SCALE_FACTOR_THRESHOLD * 100.0) + "%) change in tree length."); Log.info.println( " Use " + adjustTipHeightsInput.getName() + "='false' to override this default."); Log.info.printf(" original max tip age = %8.3f\n", maxTipHeight); Log.info.printf(" new max tip age = %8.3f\n", 0.0); Log.info.printf(" original tree length = %8.3f\n", treeLength); Log.info.printf(" new tree length = %8.3f\n", treeLength + extraTreeLength); Log.info.printf(" TL scale factor = %8.3f\n", scaleFactor); } } if (m_taxonset.get() == null && labels != null && isLabelledNewickInput.get()) { m_taxonset.setValue(new TaxonSet(Taxon.createTaxonList(labels)), this); } initStateNodes(); } // init
/** * produce JSON for an input of a beastObject, both as attribute/value pairs for primitive inputs * (if isShort=true) and as individual elements (if isShort=false) * * @param input0: name of the input * @param beastObject: beastObject to produce this input JSON for * @param buf: gets JSON results are appended * @param isShort: flag to indicate attribute/value format (true) or element format (false) * @throws Exception */ @SuppressWarnings({"rawtypes", "unchecked"}) private void inputToJSON( Input input, Object value, BEASTInterface beastObject, StringBuffer buf, boolean isShort, String indent) throws Exception { if (value != null) { // distinguish between Map, List, BEASTObject and primitive input types if (value instanceof Map) { if (!isShort) { Map<String, ?> map = (Map<String, ?>) value; StringBuffer buf2 = new StringBuffer(); // determine label width int whiteSpaceWidth = 0; for (String key : map.keySet()) { whiteSpaceWidth = Math.max(whiteSpaceWidth, key.length()); } boolean needsComma = false; List<String> keys = new ArrayList<>(); keys.addAll(map.keySet()); Collections.sort(keys); for (String key : keys) { if (needsComma) { buf2.append(",\n"); } buf2.append(indent + " " + key); for (int k = key.length(); k < whiteSpaceWidth; k++) { buf2.append(' '); } buf2.append(" :\"" + map.get(key) + "\""); needsComma = true; } buf.append(buf2); } return; } else if (value instanceof List) { if (!isShort) { StringBuffer buf2 = new StringBuffer(); // buf2.append(indent + " \"" + input0 + "\": [\n"); buf2.append(indent + " " + input.getName() + ": [\n"); boolean needsComma = false; int oldLen = buf2.length(); for (Object o2 : (List) value) { if (needsComma) { buf2.append(",\n"); } StringBuffer buf3 = new StringBuffer(); if (o2 instanceof BEASTInterface) { beastObjectToJSON((BEASTInterface) o2, input.getType(), buf3, null, false); } else { buf2.append(o2.toString()); } buf2.append(buf3); needsComma = oldLen < buf2.length(); } if (buf2.length() != oldLen) { buf.append(buf2); buf.append("\n" + indent + " ]"); } } return; } else if (value instanceof BEASTInterface) { if (!value.equals(input.defaultValue)) { // Parameters can use short hand notation if they are not in the state // Note this means lower and upper bounds are lost -- no problem for BEAST, but maybe for // BEAUti if (value instanceof Parameter.Base) { Parameter.Base parameter = (Parameter.Base) value; boolean isInState = false; for (Object o : parameter.getOutputs()) { if (o instanceof State) { isInState = true; break; } } if (!isInState) { if (isShort) { buf.append(" " + input.getName() + ": \"" + parameter.getValue() + "\""); } else { return; } } } if (isShort && isDone.contains(value)) { buf.append(" " + input.getName() + ": \"@" + ((BEASTInterface) value).getID() + "\""); if (!isInputsDone.containsKey(beastObject)) { isInputsDone.put(beastObject, new HashSet<>()); } isInputsDone.get(beastObject).add(input.getName()); } if (!isShort && (!isInputsDone.containsKey(beastObject) || !isInputsDone.get(beastObject).contains(input.getName()))) { beastObjectToJSON((BEASTInterface) value, input.getType(), buf, input.getName(), false); } } return; } else { // primitive type if (!value.equals(input.defaultValue)) { String valueString = value.toString(); if (isShort) { if (valueString.indexOf('\n') < 0) { buf.append(" " + input.getName() + ": " + normalise(input, value.toString()) + ""); } } else { if (valueString.indexOf('\n') >= 0) { buf.append( indent + "" + input.getName() + ": " + normalise(input, value.toString()) + ""); } } } return; } } else { // value=null, no JSON to produce return; } } // inputToJSON