package beast.evolution.datatype; import java.util.ArrayList; import java.util.List; import beast.core.Description; import beast.core.Input; import beast.core.Input.Validate; import beast.evolution.datatype.DataType.Base; @Description("User defined datatype. Allows custom symbols to map onto statesets.") public class UserDataType extends Base { final public Input<Integer> stateCountInput = new Input<>("states", "total number of states", Validate.REQUIRED); final public Input<Integer> codeLengthInput = new Input<>("codelength", "length of code, if negative a variable length code is assumed, default 1", 1); final public Input<String> codeMapInput = new Input<>("codeMap", "mapping of codes to states. " + "A comma separated string of codes with a subset of states. " + "A state set is a space separates list of zero based integers, up to the number of states, " + "e.g. A=0, C=1, R=0 2, ? = 0 1 2 3", Validate.REQUIRED); final public Input<String> characterNameInput = new Input<>("characterName", "the name of the character"); final public Input<String> stateNamesInput = new Input<>("value", "the list of the state names ordered " + "according to codes given, that is the first in the list is coded by 0, second, by 1 and so forth."); public UserDataType() {} // default c'tor public UserDataType(String newCharName, ArrayList<String> newStateNames) { characterNameInput.setValue(newCharName, this); if (newStateNames.size() > 0) { StringBuilder buf = new StringBuilder(); for (int i = 0; i < newStateNames.size(); i++) { buf.append(i + "=" + i +", "); } buf.append("? ="); for (int i = 0; i < newStateNames.size(); i++) { buf.append(i +" "); } codeMapInput.setValue(buf.toString(), this); buf = new StringBuilder(); for (int i = 0; i < newStateNames.size(); i++) { buf.append(newStateNames.get(i) +", "); } buf.delete(buf.length()-2, buf.length()); stateNamesInput.setValue(buf.toString(), this); stateCountInput.setValue(newStateNames.size(), this); } else { codeMapInput.setValue("", this); stateNamesInput.setValue("", this); stateCountInput.setValue(-1, this); } } @Override public void initAndValidate() { stateCount = stateCountInput.get(); codeLength = codeLengthInput.get(); String codeMapString = codeMapInput.get(); if (!codeMapInput.get().equals("")) { String[] strs = codeMapString.split(","); codeMap = ""; mapCodeToStateSet = new int[strs.length][]; int k = 0; for (String str : strs) { String[] strs2 = str.split("="); // parse the code String code = strs2[0].replaceAll("\\s", ""); codeMap += code; if (codeLength > 0) { if (code.length() != codeLength) { throw new IllegalArgumentException("Invalide code '" + code + "'. Expected code of length " + codeLength); } } else { codeMap += ","; } // parse the state set List<Integer> stateSet = new ArrayList<>(); strs2 = strs2[1].split("\\s+"); for (String str2 : strs2) { if (str2.length() > 0) { int i = Integer.parseInt(str2); if (i < 0 || (stateCount > 0 && i >= stateCount)) { throw new IllegalArgumentException("state index should be from 0 to statecount, not " + i); } stateSet.add(i); } } int[] stateSet2 = new int[stateSet.size()]; for (int i = 0; i < stateSet.size(); i++) { stateSet2[i] = stateSet.get(i); } mapCodeToStateSet[k++] = stateSet2; } } } @Override public String getCode(int state) { return String.valueOf(codeMap.split(",")[state]); } @Override public String getTypeDescription() { return "user defined"; } }