Java tutorial
/* * Copyright (c) 2014 Patrick Meyer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.itemanalysis.jmetrik.file; import com.itemanalysis.psychometrics.data.DataType; import com.itemanalysis.psychometrics.data.VariableAttributes; import com.itemanalysis.psychometrics.data.VariableName; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVPrinter; import java.io.*; import java.nio.file.Files; import java.nio.file.Path; import java.util.LinkedHashMap; /** * A *.jmetrik file is a plain text file that is created by this class. The file is written in jMetrik format. * This format includes a header with information (VariableAttributes) about the file, variables contained in * the file, item scoring information, and information about special data codes (see writeHeader()). The * header is followed by one or more rows of comma delimited values. There is one row for each case. * * For example, the *.jmetrik file would appear as: * * # VERSION * jmetrik1 * # METADATA * 5 * # ATTRIBUTES * id,INTEGER,,"(NA,NR,OM)(0.0,0.0,0.0)",, * gender,STRING,,"(NA,NR,OM)(0.0,0.0,0.0)",, * race,STRING,,"(NA,NR,OM)(0.0,0.0,0.0)",, * item1,STRING,"(A,B,C,D)(1.0,0.0,0.0,0.0)","(NA,NR,OM)(0.0,0.0,0.0)",, * item2,STRING,"(A,B,C,D)(0.0,0.0,0.0,1.0)","(NA,NR,OM)(0.0,0.0,0.0)",, * item3,STRING,"(A,B,C,D)(0.0,0.0,1.0,0.0)","(NA,NR,OM)(0.0,0.0,0.0)",, * item4,STRING,"(A,B,C,D)(0.0,1.0,0.0,0.0)","(NA,NR,OM)(0.0,0.0,0.0)",, * sumscore,INTEGER,,"(NA,NR,OM)(0.0,0.0,0.0)",, * # DATA * 1,M,W,A,A,D,D,35 * 2,F,W,A,B,C,C,30 * 3,M,W,A,C,D,B,32 * 4,M,W,A,B,D,B,23 * 5,M,W,A,D,C,B,35 * */ public class JmetrikFileWriter implements AutoCloseable { private LinkedHashMap<VariableName, VariableAttributes> variableAttributeMap = null; private boolean printScoredValues = false; private Path file = null; private BufferedWriter writer = null; private CSVPrinter printer = null; // private String[] line = null; private LinkedHashMap<VariableName, String> variableValueMap = null; // int length = 0; /** * Default constructor that provides the options of printing scored values. * * @param file Output file. * @param variableAttributeMap a map of variables to be written to file. Values in the output will be ordered according to the * getOrder() method in VariableAttributes. * @param printScoredValues true if writer should print scored item values. Prints the original item response if false. */ public JmetrikFileWriter(Path file, LinkedHashMap<VariableName, VariableAttributes> variableAttributeMap, boolean printScoredValues) { this.file = file; this.variableAttributeMap = variableAttributeMap; this.printScoredValues = printScoredValues; // this.length = this.variableAttributeMap.size(); // line = new String[this.length]; variableValueMap = new LinkedHashMap<VariableName, String>(); } public JmetrikFileWriter(File file, LinkedHashMap<VariableName, VariableAttributes> variableAttributeMap, boolean printScoredValues) { this(file.toPath(), variableAttributeMap, printScoredValues); } public JmetrikFileWriter(File file, LinkedHashMap<VariableName, VariableAttributes> variableAttributeMap) { this(file.toPath(), variableAttributeMap, false); } /** * Constructor to use when not writing scored values to file. * * @param file output file. * @param variableAttributeMap a map of variables to be written to file. Values in the output will be ordered according to the * getOrder() method in VariableAttributes. */ public JmetrikFileWriter(Path file, LinkedHashMap<VariableName, VariableAttributes> variableAttributeMap) { this(file, variableAttributeMap, false); } /** * Opens a connection to the file by instantiating a OutputStreamWriter and a CSVPrinter. * Assumes the output file is not a temporary file that should be deleted. The output file * is a permanent file. * * @throws IOException */ public void openConnection() throws IOException { writer = new BufferedWriter(new OutputStreamWriter(Files.newOutputStream(file))); printer = new CSVPrinter(writer, CSVFormat.DEFAULT.withCommentMarker('#')); } /** * Closes the connection to the data file. This method implements the AutoCloseable interface. * * @throws IOException */ public void close() throws IOException { if (printer != null) printer.close(); if (writer != null) writer.close(); } /** * Writes the file header that contains the variable attributes and other meta data for the file. * * @param nrow number of rows in the data file. * @throws IOException */ public void writeHeader(int nrow) throws IOException { //Writer header to file printer.printComment("VERSION"); printer.printRecord(new String[] { "jmetrik1" }); printer.printComment("METADATA"); printer.printRecord(new String[] { Integer.valueOf(nrow).toString() }); printer.printComment("ATTRIBUTES"); for (VariableName v : variableAttributeMap.keySet()) { printer.printRecord(variableAttributeMap.get(v).getAttributeArray()); } printer.printComment("DATA"); printer.flush(); } /** * Writes a new value with the option of writing a scored value instead of the original value. * This method adds the value to an array but does not write it to file. Call updateRow() to write * the array to file. * * @param variableName name of variable being written * @param value value to be written * @throws IllegalArgumentException */ public void writeValue(VariableName variableName, String value) throws IllegalArgumentException { VariableAttributes variableAttributes = variableAttributeMap.get(variableName); if (variableAttributes == null) throw new IllegalArgumentException("Variable not found in header: " + variableName.toString()); //if a numeric variable, can't write String value if (variableAttributes.getDataType() != DataType.STRING) { //value must be parsable as double or int if (!variableAttributes.isMissing(value)) { try { Double.parseDouble(value); } catch (NumberFormatException ex) { throw new IllegalArgumentException( "Variable must have numeric values: " + variableName.toString()); } } } // int pos = variableAttributes.positionInDb(); if (printScoredValues) { variableValueMap.put(variableName, Double.valueOf(variableAttributes.computeItemScore(value)).toString()); // line[pos] = Double.valueOf(variableAttributes.computeItemScore(value)).toString(); } else { variableValueMap.put(variableName, value); // line[pos] = value; } } /** * Writes a new value with the option of writing a scored value instead of the original value. * This method adds the value to an array but does not write it to file. Call updateRow() to write * the array to file. * * @param variableName name of variable being written * @param value value to be written * @throws IllegalArgumentException */ public void writeValue(VariableName variableName, double value) throws IllegalArgumentException { VariableAttributes variableAttributes = variableAttributeMap.get(variableName); if (variableAttributes == null) throw new IllegalArgumentException("Variable not found in header: " + variableName.toString()); // int pos = variableAttributes.positionInDb(); if (printScoredValues) { variableValueMap.put(variableName, Double.valueOf(variableAttributes.computeItemScore(value)).toString()); // line[pos] = Double.valueOf(variableAttributes.computeItemScore(value)).toString(); } else { variableValueMap.put(variableName, Double.valueOf(value).toString()); // line[pos] = Double.valueOf(value).toString(); } } /** * Writes a new value with the option of writing a scored value instead of the original value. * This method adds the value to an array but does not write it to file. Call updateRow() to write * the array to file. * * @param variableName name of variable being written * @param value value to be written * @throws IllegalArgumentException */ public void writeValue(VariableName variableName, int value) throws IllegalArgumentException { VariableAttributes variableAttributes = variableAttributeMap.get(variableName); if (variableAttributes == null) throw new IllegalArgumentException("Variable not found in header: " + variableName.toString()); int pos = variableAttributes.positionInDb(); if (printScoredValues) { variableValueMap.put(variableName, Double.valueOf(variableAttributes.computeItemScore(value)).toString()); // line[pos] = Double.valueOf(variableAttributes.computeItemScore(value)).toString(); } else { variableValueMap.put(variableName, Integer.valueOf(value).toString()); // line[pos] = Integer.valueOf(value).toString(); } } /** * Writes a new value with the option of writing a scored value instead of the original value. * This method adds the value to an array but does not write it to file. Call updateRow() to write * the array to file. * * @param index column position of the variable being written * @param value value to be written */ // public void writeValue(int index, String value){ // line[index] = value; // } /** * Writes a new value with the option of writing a scored value instead of the original value. * This method adds the value to an array but does not write it to file. Call updateRow() to write * the array to file. * * @param index column position of the variable being written * @param value value to be written */ // public void writeValue(int index, double value){ // line[index] = Double.valueOf(value).toString(); // } /** * Writes a new value with the option of writing a scored value instead of the original value. * This method adds the value to an array but does not write it to file. Call updateRow() to write * the array to file. * * @param index column position of the variable being written * @param value value to be written */ // public void writeValue(int index, int value){ // line[index] = Integer.valueOf(value).toString(); // } /** * Write the value array to file and moves to the next line. * * @throws IOException */ public void updateRow() throws IOException { //Loop over all variable in map. If value exists for it, write the value. Otherwise, write an empty string. for (VariableName v : variableAttributeMap.keySet()) { String s = variableValueMap.get(v); if (null == s) { printer.print(""); } else { printer.print(variableValueMap.get(v)); } } variableValueMap.clear(); printer.println(); // for(String s : line){ // printer.print(s); // } // line = new String[length]; // printer.println(); } }