package jalview.datamodel;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import java.util.Scanner;
/**
- * Data structure to hold a HMM file
- */
-/**
+ * Data structure which stores a hidden Markov model. Currently contains file properties as well, not sure whether these should be transferred to the HMMFile class
+ *
* @author TZVanaalten
*
*/
public class HiddenMarkovModel
{
+ // Stores file properties. Do not directly access this field as it contains
+ // only string value - use the getter methods. For example, to find the length
+ // of theHMM, use getModelLength()to return an int value
+ Map<String, String> fileProperties = new HashMap<>();
+
+ // contains the average emission probabilities for each symbol
+ List<Double> averageMatchStateEmissionProbabilities = new ArrayList<>();
+
+ // contains the probabilities of insert 0 emissions for each symbol
+ List<Double> insertZeroEmissions = new ArrayList<>();
+
+ // contains the probabilities of transitions from the begin state and insert
+ // state 0. These are bm, bi, bd, im, ii, dm and dd in order (0th position in
+ // the array indicates the probability of a bm transition)
+
+ List<Double> beginStateTransitions = new ArrayList<>();
+
+ // contains the alignment column index for each node
+ List<Integer> alignmentColumnIndexes = new ArrayList<>();
+
+ // contains all other annotations for each node. These can be the
+ // consensus(CONS), reference annotation(RF), mask value(MM) or consensus
+ // structure(CS)
+ List<HashMap<String, Character>> annotations = new ArrayList<>();
+
+ // contains the match emission for each symbol at each node
+ List<List<Double>> matchEmissions = new ArrayList<>();
+
+ // contains the insert emission for each symbol at each node
+ List<List<Double>> insertEmissions = new ArrayList<>();
+
+ // contains the state transition for each state transition. See
+ // beginStateTransitions field for transition possibilities.
+ List<List<Double>> stateTransitions = new ArrayList<>();
+
+ // contains cutoffs and thresholds from PFAM
+ Map<String, Double[]> pfamData = new HashMap<>();
+
+ // contains e-value statistic objects which contain the alignment mode
+ // configuration, and the slope and location of each distribution
+ Map<String, EValueStatistic> eValueStatistics = new HashMap<>();
+
+ final String yes = "yes";
+
+ final String no = "no";
+
+ List<Character> symbols = new ArrayList<>();
+
+ public List<Double> getBeginStateTransitions()
+ {
+ return beginStateTransitions;
+ }
+
+ public void setBeginStateTransitions(List<Double> beginStateTransitionsL)
+ {
+ this.beginStateTransitions = beginStateTransitionsL;
+ }
+
+ public List<List<Double>> getStateTransitions()
+ {
+ return stateTransitions;
+ }
+
+ public void setStateTransitions(List<List<Double>> stateTransitionsL)
+ {
+ this.stateTransitions = stateTransitionsL;
+ }
+
+ public List<Character> getSymbols()
+ {
+ return symbols;
+ }
+
+ public void setSymbols(List<Character> symbolsL)
+ {
+ this.symbols = symbolsL;
+ }
+
+ public List<Double> getAverageMatchStateEmissionProbabilities()
+ {
+ return averageMatchStateEmissionProbabilities;
+ }
+
+ public void setAverageMatchStateEmissionProbabilities(
+ List<Double> averageMatchStateEmissionProbabilitiesL)
+ {
+ this.averageMatchStateEmissionProbabilities = averageMatchStateEmissionProbabilitiesL;
+ }
+
+
+ public List<Double> getInsertZeroEmissions()
+ {
+ return insertZeroEmissions;
+ }
+
+ public void setInsertZeroEmissions(List<Double> insertZeroEmissionsL)
+ {
+ this.insertZeroEmissions = insertZeroEmissionsL;
+ }
+
+ public List<List<Double>> getMatchEmissions()
+ {
+ return matchEmissions;
+ }
+
+ public void setMatchEmissions(List<List<Double>> matchEmissionsL)
+ {
+ this.matchEmissions = matchEmissionsL;
+ }
+
+ public List<List<Double>> getInsertEmissions()
+ {
+ return insertEmissions;
+ }
- // Stores file properties
- private Map<String, String> fileProperties = new HashMap<>();
+ public void setInsertEmissions(List<List<Double>> insertEmissionsL)
+ {
+ this.insertEmissions = insertEmissionsL;
+ }
+ public void fillSymbols(String line)
+ {
+ Scanner scanner = new Scanner(line);
+ scanner.next();
+ while (scanner.hasNext())
+ {
+ symbols.add(scanner.next().charAt(0));
+ }
+ scanner.close();
+ }
+ public String getName()
+ {
+ return fileProperties.get("NAME");
+ }
public String getAccessionNumber()
{
return fileProperties.get("ACC");
}
+ public void setAccessionNumber(String value)
+ {
+ fileProperties.put("ACC", value);
+ }
+
public String getDescription()
{
return fileProperties.get("DESC");
}
- public int getModelLength()
+ public void setDescription(String value)
{
+ fileProperties.put("DESC", value);
+ }
+
+ public Integer getLength()
+ {
+ if (fileProperties.get("LENG") == null)
+ {
+ return null;
+ }
return Integer.parseInt(fileProperties.get("LENG"));
}
- public int getMaxInstanceLength()
+ public void setLength(int value)
+ {
+ fileProperties.put("LENG", String.valueOf(value));
+ }
+
+ public Integer getMaxInstanceLength()
{
+ if (fileProperties.get("MAXL") == null)
+ {
+ return null;
+ }
return Integer.parseInt(fileProperties.get("MAXL"));
}
+ public void setMaxInstanceLength(int value)
+ {
+ fileProperties.put("MAXL", String.valueOf(value));
+ }
+
// gets type of symbol alphabet - "amino", "DNA", "RNA"
public String getAlphabetType()
{
return fileProperties.get("ALPH");
}
+ public void setAlphabetType(String value)
+ {
+ fileProperties.put("ALPH", value);
+ }
+
// returns boolean indicating whether the reference annotation character field
// for each match state is valid or ignored
public boolean getReferenceAnnotationFlag()
{
- if (fileProperties.get("RF") == "yes")
+ if (fileProperties.get("RF") != null)
{
- return true;
+ if (fileProperties.get("RF").equals(yes))
+ {
+ return true;
+ }
}
return false;
}
+ public void setReferenceAnnotationFlag(boolean value)
+ {
+ if (value)
+ {
+ fileProperties.put("RF", yes);
+ }
+ else
+ {
+ fileProperties.put("RF", no);
+ }
+
+ }
+
// returns boolean indicating whether the model mask annotation character
// field
// for each match state is valid or ignored
public boolean getModelMaskedFlag()
{
- if (fileProperties.get("MM") == "yes")
+ if (fileProperties.get("MM") != null)
{
- return true;
+ if (fileProperties.get("MM").equals(yes))
+ {
+ return true;
+ }
}
return false;
}
+ public void setModelMaskedFlag(boolean value)
+ {
+ if (value)
+ {
+ fileProperties.put("MM", yes);
+ }
+ else
+ {
+ fileProperties.put("MM", no);
+ }
+ }
+
// returns boolean indicating whether the consensus residue field
// for each match state is valid or ignored
public boolean getConsensusResidueAnnotationFlag()
{
- if (fileProperties.get("CONS") == "yes")
+ if (fileProperties.get("CONS") != null)
{
- return true;
+ if (fileProperties.get("CONS").equals(yes))
+ {
+ return true;
+ }
}
return false;
}
+ public void setConsensusResidueeAnnotationFlag(boolean value)
+ {
+ if (value)
+ {
+ fileProperties.put("CONS", yes);
+ }
+ else
+ {
+ fileProperties.put("CONS", no);
+ }
+ }
+
// returns boolean indicating whether the consensus structure character field
// for each match state is valid or ignored
public boolean getConsensusStructureAnnotationFlag()
{
- if (fileProperties.get("CS") == "yes")
+ if (fileProperties.get("CS") != null)
{
- return true;
+ if (fileProperties.get("CS").equals(yes))
+ {
+ return true;
+ }
}
return false;
}
+ public void setConsensusStructureAnnotationFlag(boolean value)
+ {
+ if (value)
+ {
+ fileProperties.put("CS", yes);
+ }
+ else
+ {
+ fileProperties.put("CS", no);
+ }
+ }
+
// returns boolean indicating whether the model mask annotation character
// field
// for each match state is valid or ignored
public boolean getMapAnnotationFlag()
{
- if (fileProperties.get("MAP") == "yes")
+ if (fileProperties.get("MAP") != null)
{
- return true;
+ if (fileProperties.get("MAP").equals(yes))
+ {
+ return true;
+ }
}
return false;
}
- // not sure whether to implement this
- // public Date getDate()
- // {
+ public void setMapAnnotationFlag(boolean value)
+ {
+ if (value)
+ {
+ fileProperties.put("MAP", yes);
+ }
+ else
+ {
+ fileProperties.put("MAP", no);
+ }
+ }
- // }
+ // not sure whether to implement this with Date object
+ public String getDate()
+ {
+ return fileProperties.get("DATE");
+ }
+
+ public void setDate(String value)
+ {
+ fileProperties.put("DATE", value);
+ }
// not sure whether to implement this
- // public String getCommandLineLog()
- // {
+ public String getCommandLineLog()
+ {
+ return fileProperties.get("COM");
+ }
- // }
+ public void setCommandLineLog(String value)
+ {
+ fileProperties.put("COM", value);
+ }
// gets the number of sequences that the HMM was trained on
- public int getSequenceNumber()
+ public Integer getSequenceNumber()
{
+ if (fileProperties.get("NSEQ") == null)
+ {
+ return null;
+ }
return Integer.parseInt(fileProperties.get("NSEQ"));
}
+ public void setSequenceNumber(int value)
+ {
+ fileProperties.put("NSEQ", String.valueOf(value));
+ }
+
// gets the effective number determined during sequence weighting
- public int getEffectiveSequenceNumber()
+ public Double getEffectiveSequenceNumber()
+ {
+ if (fileProperties.get("LENG") == null)
+ {
+ return null;
+ }
+ return Double.parseDouble(fileProperties.get("EFFN"));
+ }
+
+ public void setEffectiveSequenceNumber(double value)
+ {
+ fileProperties.put("EFFN", String.valueOf(value));
+ }
+
+ public Long getCheckSum()
+ {
+ if (fileProperties.get("LENG") == null)
+ {
+ return null;
+ }
+ return Long.parseLong(fileProperties.get("CKSUM"));
+ }
+
+ public void setCheckSum(long value)
+ {
+ fileProperties.put("CKSUM", String.valueOf(value));
+ }
+
+ public Double getGatheringThreshold1()
+ {
+ try
+ {
+ return pfamData.get("GA")[0];
+ } catch (NullPointerException e)
+ {
+ return null;
+ }
+ }
+
+ public void setPFAMData(String key, Double[] data)
+ {
+ pfamData.put(key, data);
+ }
+
+ public Double getGatheringThreshold2()
+ {
+ try
+ {
+ return pfamData.get("GA")[1];
+ } catch (NullPointerException e)
+ {
+ return null;
+ }
+
+ }
+
+ public Double getTrustedCutoff1()
+ {
+ try
+ {
+ return pfamData.get("TC")[0];
+ } catch (NullPointerException e)
+ {
+ return null;
+ }
+
+ }
+
+ public Double getTrustedCutoff2()
{
- return Integer.parseInt(fileProperties.get("EFFN"));
+ try
+ {
+ return pfamData.get("TC")[1];
+ } catch (NullPointerException e)
+ {
+ return null;
+ }
+
}
- public int getCheckSum()
+ public Double getNoiseCutoff1()
{
- return Integer.parseInt(fileProperties.get("CKSUM"));
+ try
+ {
+ return pfamData.get("NC")[0];
+ } catch (NullPointerException e)
+ {
+ return null;
+ }
+
}
- // need to ask if BigDecimal is best decimal type for this purpose
- // and how to limit number of decimals
- public double getGatheringThresholdGA1()
+ public Double getNoiseCutoff2()
{
- return Double.parseDouble((fileProperties.get("GA1")));
+ try
+ {
+ return pfamData.get("NC")[1];
+ } catch (NullPointerException e)
+ {
+ return null;
+ }
+
+ }
+
+ public String getAlignmentModeConfiguration(String key)
+ {
+ return eValueStatistics.get(key).alignmentModeConfiguration;
+ }
+
+ public Double getSlopeOfDistribution(String scoreDistribution)
+ {
+ try
+ {
+ return eValueStatistics.get(scoreDistribution).slopeOfDistribution;
+ } catch (NullPointerException e)
+ {
+ return null;
+ }
+ }
+
+ public Double getLocationOfDistribution(String scoreDistribution)
+ {
+ try
+ {
+ return eValueStatistics.get(scoreDistribution).locationOfDistribution;
+ } catch (NullPointerException e)
+ {
+ return null;
+ }
}
+ public void addStatistic(String name, EValueStatistic stats)
+ {
+ eValueStatistics.put(name, stats);
+ }
+
+ /**
+ * public double getBeginStateTransitions(Character symbol) { return
+ * beginStateTransitions.get(symbol); }
+ **/
+
public void put(String key, String value)
{
fileProperties.put(key, value);
}
+ public Map<String, EValueStatistic> getEValueStatistics()
+ {
+ return eValueStatistics;
+ }
+
+ public void setEValueStatistics(
+ Map<String, EValueStatistic> eValueStatisticsM)
+ {
+ this.eValueStatistics = eValueStatisticsM;
+ }
+
+ public List<Integer> getAlignmentColumnIndexes()
+ {
+ return alignmentColumnIndexes;
+ }
+
+ public void setAlignmentColumnIndexes(
+ List<Integer> alignmentColumnIndexesL)
+ {
+ this.alignmentColumnIndexes = alignmentColumnIndexesL;
+ }
+
+ public List<HashMap<String, Character>> getAnnotations()
+ {
+ return annotations;
+ }
+
+ public void setAnnotations(List<HashMap<String, Character>> annotationsL)
+ {
+ this.annotations = annotationsL;
+ }
+
+ public Map<String, String> getFileProperties()
+ {
+ return fileProperties;
+ }
+
+ public void setFileProperties(Map<String, String> fileProperties)
+ {
+ this.fileProperties = fileProperties;
+ }
}
+
--- /dev/null
+package jalview.io;
+
+import jalview.datamodel.EValueStatistic;
+import jalview.datamodel.HiddenMarkovModel;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+
+/**
+ * reads in and writes out a HMMER standard file
+ *
+ *
+ * @author TZVanaalten
+ *
+ */
+public class HMMFile extends FileParse
+{
+ // HMM to store file data
+ HiddenMarkovModel hmm = new HiddenMarkovModel();
+
+ // Source of file
+ String dataObject;
+
+ // number of symbols
+ int numberOfSymbols;
+
+ // number of possible transitions
+ final int NUMBER_OF_TRANSITIONS = 7;
+
+ // file header
+ String fileHeader;
+
+ /**
+ * Constructor which contains model to be filled or exported
+ *
+ * @param dataSource
+ * Filename, URL or Pasted String to read from
+ */
+ public HMMFile(String dataSource)
+ {
+ dataObject = dataSource;
+ }
+
+ /**
+ * reads data from HMM file
+ *
+ * @throws IOException
+ */
+ public void parse() throws IOException
+ {
+ File file = new File(dataObject);
+ FileReader fr = new FileReader(file);
+ BufferedReader br = new BufferedReader(fr);
+ parseFileProperties(br);
+ parseModel(br);
+
+ }
+
+ /**
+ * imports file properties from hmm file
+ *
+ * @param input
+ * buffered reader used to read in file
+ * @throws IOException
+ */
+ public void parseFileProperties(BufferedReader input) throws IOException
+ {
+ boolean readingFile = true;
+ fileHeader = input.readLine();
+ String line = input.readLine();
+ while (readingFile)
+ {
+ if (line != null)
+ {
+ Scanner parser = new Scanner(line);
+ String next = parser.next();
+ if ("HMM".equals(next)) // indicates start of HMM data (end of file
+ // properties)
+ {
+ readingFile = false;
+ hmm.fillSymbols(line);
+ numberOfSymbols = hmm.getSymbols().size();
+ }
+ else if ("STATS".equals(next)) // reads e-value stats into separate
+ // field
+ // on HMM object
+ {
+ readStats(parser);
+ }
+ else if ("GA".equals(next) || "TC".equals(next)
+ || "NC".equals(next)) // reads
+ // pfam
+ // data
+ // into
+ // separate
+ // field
+ // on
+ // HMM
+ // object
+ {
+ Double[] data = new Double[2];
+ data[0] = parser.nextDouble();
+ data[1] = parser.nextDouble();
+ hmm.setPFAMData(next, data);
+ }
+ else
+ {
+ String key = next;
+ String value = parser.next();
+ while (parser.hasNext())
+ {
+ value = value + " " + parser.next();
+ }
+ hmm.put(key, value);
+ }
+ parser.close();
+ }
+ line = input.readLine();
+ if (line == null)
+ {
+ readingFile = false;
+ }
+ }
+
+ }
+
+ /**
+ * creates a new EValueStatistic object to store stats
+ *
+ * @param parser
+ * Scanner which contains data for STATS line
+ *
+ */
+ public void readStats(Scanner parser)
+ {
+ if (parser.hasNext())
+ {
+ String name;
+ double slope;
+ double location;
+ String configuration;
+
+ configuration = parser.next();
+ name = parser.next();
+ slope = parser.nextDouble();
+ location = parser.nextDouble();
+ hmm.addStatistic(name,
+ new EValueStatistic(configuration, slope, location));
+ }
+ }
+
+ /**
+ * parses the model data from the hmm file
+ *
+ * @param input
+ * buffered reader used to read file
+ * @throws IOException
+ */
+ public void parseModel(BufferedReader input) throws IOException
+ {
+
+ String line = input.readLine();
+ Scanner scanner = new Scanner(line);
+ String next = scanner.next();
+ if ("COMPO".equals(next)) // checks to and stores COMPO data if present
+ {
+ for (int i = 0; i < numberOfSymbols; i++)
+
+ {
+ hmm.getAverageMatchStateEmissionProbabilities()
+ .add(scanner.nextDouble());
+ }
+ }
+ scanner.close();
+ parseBeginNodeData(input);
+ for (int i = 0; i < hmm.getLength(); i++)
+ {
+ Scanner matchReader = new Scanner(input.readLine());
+ matchReader.nextInt(); // skips number indicating position in HMM
+ hmm.getMatchEmissions()
+ .add(fillList(matchReader, numberOfSymbols));
+ parseAnnotations(matchReader, i);
+ matchReader.close();
+ Scanner insertReader = new Scanner(input.readLine());
+ hmm.getInsertEmissions().add(fillList(insertReader, numberOfSymbols));
+ insertReader.close();
+ Scanner transitionReader = new Scanner(input.readLine());
+ hmm.getStateTransitions()
+ .add(fillList(transitionReader, NUMBER_OF_TRANSITIONS));
+ transitionReader.close();
+ }
+
+ }
+
+ /**
+ * parses the begin state transitions and insert 0 emissions
+ *
+ * @param input
+ * buffered reader used to read model
+ * @param currentline
+ * string contain all data on current line of buffered reader
+ * @throws IOException
+ */
+
+ public void parseBeginNodeData(BufferedReader input)
+ throws IOException
+ {
+ Scanner scanner = new Scanner(input.readLine());
+ hmm.setInsertZeroEmissions(fillList(scanner, hmm.getSymbols().size()));
+ scanner.close();
+ Scanner scannerTransitions = new Scanner(input.readLine());
+ hmm.setBeginStateTransitions(
+ fillList(scannerTransitions, NUMBER_OF_TRANSITIONS));
+ scannerTransitions.close();
+ }
+
+ /**
+ * parses annotations on match emission line
+ *
+ * @param scanner
+ * scanner which is processing match emission line
+ * @param index
+ * index of node which is beign scanned
+ */
+ public void parseAnnotations(Scanner scanner, int index)
+ {
+ if (hmm.getMapAnnotationFlag())
+ {
+ hmm.getAlignmentColumnIndexes().add(scanner.nextInt());
+ }
+ else
+ {
+ scanner.next();
+ }
+ hmm.getAnnotations().add(new HashMap<String, Character>());
+ hmm.getAnnotations().get(index).put("CONS", scanner.next().charAt(0));
+ hmm.getAnnotations().get(index).put("RF", scanner.next().charAt(0));
+ hmm.getAnnotations().get(index).put("MM", scanner.next().charAt(0));
+ hmm.getAnnotations().get(index).put("CS", scanner.next().charAt(0));
+ }
+ /**
+ *
+ * @param transition
+ * type of transition occuring
+ * @return index value representing position along stateTransition array.
+ */
+ public Integer getTransitionType(String transition)
+ {
+ Integer index;
+ switch (transition)
+ {
+ case "mm":
+ index = 0;
+ break;
+ case "mi":
+ index = 1;
+ break;
+ case "md":
+ index = 2;
+ break;
+ case "im":
+ index = 3;
+ break;
+ case "ii":
+ index = 4;
+ break;
+ case "dm":
+ index = 5;
+ break;
+ case "dd":
+ index = 6;
+ break;
+ default:
+ index = null;
+ }
+ return index;
+ }
+
+ /**
+ *
+ * @param input
+ * scanner for line containing data to be transferred to list
+ * @param numberOfElements
+ * number of elements in the list to be filled
+ * @return filled list
+ */
+ public static List<Double> fillList(Scanner input,
+ int numberOfElements)
+ {
+ List<Double> list = new ArrayList<>();
+ String next;
+ for (int i = 0; i < numberOfElements; i++)
+ {
+ next = input.next();
+ if (next.contains("*")) // state transitions to or from delete states
+ // occasionally have values of -infinity. These
+ // values are represented by an * in the .hmm
+ // file, and by a null value in the
+ // HiddenMarkovModel class
+ {
+ list.add(null);
+ }
+ else
+ {
+ list.add(Double.valueOf(next));
+ }
+ }
+ return list;
+ }
+
+ /**
+ * writes a HiddenMarkovModel to a file. Needs mode work to make file more
+ * readable for humans (align columns)
+ *
+ * @param exportLocation
+ * Filename, URL or Pasted String to write to
+ * @throws FileNotFoundException
+ * @throws UnsupportedEncodingException
+ */
+ public void exportFile(String exportLocation)
+ throws FileNotFoundException, UnsupportedEncodingException
+ {
+ PrintWriter writer = new PrintWriter(exportLocation, "UTF-8");
+ writer.println(fileHeader);
+ for (Map.Entry<String, String> entry : hmm.getFileProperties()
+ .entrySet())
+ {
+ writer.println(entry.getKey() + " " + entry.getValue());
+ }
+ writer.println(
+ "HMM" + " " + convertCharListToString(hmm.getSymbols()));
+ writer.println("m->m m->i m->d i->m i->i d->m d->d");
+ if (false == hmm.getAverageMatchStateEmissionProbabilities().isEmpty())
+ {
+ writer.println("COMPO" + " " + convertDoubleListToString(
+ hmm.getAverageMatchStateEmissionProbabilities()));
+ }
+ writer.println(convertDoubleListToString(hmm.getInsertZeroEmissions()));
+ writer.println(
+ convertDoubleListToString(hmm.getBeginStateTransitions()));
+
+ for (Integer i = 0; i < hmm.getLength(); i++)
+ {
+ String matchEmissionLine = i.toString() + " "; // adds node index
+ matchEmissionLine += convertDoubleListToString(
+ hmm.getMatchEmissions().get(i)); // adds match emissions
+ matchEmissionLine += " "
+ + hmm.getAlignmentColumnIndexes().get(i).toString(); // adds MAP
+ // annotation
+ matchEmissionLine += " "
+ + hmm.getAnnotations().get(i).get("CONS").toString(); // adds CONS
+ // annotation
+ matchEmissionLine += " "
+ + hmm.getAnnotations().get(i).get("RF").toString(); // adds RF
+ // annotation
+ matchEmissionLine += " "
+ + hmm.getAnnotations().get(i).get("MM").toString(); // adds MM
+ // annotation
+ matchEmissionLine += " "
+ + hmm.getAnnotations().get(i).get("CS").toString(); // adds CS
+ // annotation
+ writer.println(matchEmissionLine);
+
+ writer.println(
+ convertDoubleListToString(hmm.getInsertEmissions().get(i)));
+ writer.println(
+ convertDoubleListToString(hmm.getStateTransitions().get(i)));
+ }
+ writer.println("//");
+
+ writer.close();
+ }
+
+ /**
+ * converts an list of characters to a string with items separated by spaces
+ *
+ * @param list
+ * character list to be converted
+ * @return string value of char list
+ */
+ public String convertCharListToString(List<Character> list)
+ {
+ String string = "";
+ for (Character item : list)
+ {
+ string = string + item.toString() + " ";
+ }
+
+ return string;
+ }
+
+ /**
+ * converts an list of doubles to a string with items separated by spaces
+ *
+ * @param list
+ * double list to be converted
+ * @return string value of double list
+ */
+ public String convertDoubleListToString(List<Double> list)
+ {
+ String string = "";
+ for (Double item : list)
+ {
+ if (item != null)
+ {
+ string = string + item.toString() + " ";
+ }
+ else
+ {
+ string = string + "*" + " ";
+ }
+
+ }
+
+ return string;
+ }
+}
+