/*
* ALMA - Atacama Large Millimiter Array
* (c) European Southern Observatory, 2009
* Copyright by ESO (in the framework of the ALMA collaboration)
* and Cosylab 200, All rights reserved
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
package alma.acs.logging.tools;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Vector;
import com.cosylab.logging.engine.log.ILogEntry;
import com.cosylab.logging.engine.log.LogTypeHelper;
import com.cosylab.logging.engine.log.LogField;
import alma.acs.util.IsoDateFormat;
/**
* Objects of this class produce a CSV string from a given log.
* The CSV adhere to the definition in RFC4180.
* <P>
* It is possible to select the columns of the log to export and their
* positions by setting a string.
* <P>
* A method generate the header in CSV format
* <P>
* All the fields appear inside double quotes.
* If a field contains double quotes they are escaped by double quotes.
* It is also possible not to enclose the fields by double quotes but in that
* case the content of the field is changed because some character are not
* allowed.
* <P>
* It is possible to define a different separator instead of a comma, like
* for example the TAB.
*
* @author acaproni
*
*/
public class CSVConverter extends LogConverter {
/**
* The separator, usually a ','
*/
private char separator=',';
/**
* If <code>true</code> each field is enclosed in double quotes
* otherwise the double quotes do not enclose the fields but
* some character in the fields will be replaced because
* are not allowed (like the double quotes for example)
*/
private boolean useDoubleQuotes = true;
/**
* Constructor
*
* @param cols A string describing the field of the log and their
* position in the output
*/
public CSVConverter(String cols) {
super(cols);
}
/**
* Constructor.
* <P>
* The converted string contains all the fields just once
* and the additional data.
*
*/
public CSVConverter() {
super();
}
/**
* Constructor
*
* @param cols A string describing the field of the log and their
* position in the output
* @param separator A character to use as fields separator
* @param doubleQuotes If <code>true</code> the fields are enclosed by double quotes
*/
public CSVConverter(String cols, char separator, boolean doubleQuotes) {
super(cols);
useDoubleQuotes=doubleQuotes;
this.separator=separator;
}
/**
* Set the separator
*
* @param sep The new separator char
*/
public void setSeparator(char sep) {
separator=sep;
}
/**
* Put or remove the double quotes around the fields
*
* @param enclose If <code>true</code> the fields are enclosed by double quotes
*/
public void encloseByDoubleQuotes(boolean enclose) {
useDoubleQuotes=enclose;
}
/**
* Generate the header for the CSV file
* (it is optional and can appear in the first line of the file)
*
* @return The CSV string representing the header in CSV format
* with CR/LF at the end of the line
*/
public String getHeader() {
StringBuilder str = new StringBuilder();
for (int t=0; t<colIndex.length(); t++) {
Character c= Character.toUpperCase(colIndex.charAt(t));
int index;
if ((c>='0' && c<='9') || (c>='A' && c<='F')) {
index=Integer.parseInt(c.toString(),16);
} else {
index=16; // DATA
}
if (t>0) {
str.append(separator);
}
appendField(LogField.values()[index].getName(),str);
}
str.append('\n');
return str.toString();
}
/**
* Convert a log in a CSV string
*
* @param log The log to convert
* @return The CSV string representing the log
*/
public String convert(ILogEntry log) {
if (log==null) {
throw new IllegalArgumentException("Impossible to convert a null log");
}
StringBuilder str = new StringBuilder();
SimpleDateFormat df = new IsoDateFormat();
for (int t=0; t<colIndex.length(); t++) {
if (t>0) {
str.append(separator);
}
Character c= Character.toUpperCase(colIndex.charAt(t));
if (
(c>='0' && c<='9') ||
(c>='A' && c<=Character.toUpperCase(LogField.values()[LogField.values().length-1].id))) {
LogField field=LogField.fromID(c);
Object obj = log.getField(field);
if (obj==null) {
appendField(null,str);
} else if (field==LogField.TIMESTAMP) {
// Write the date in the right format
Date dt=new Date(((Long)obj).longValue());
StringBuffer dateSB = new StringBuffer();
java.text.FieldPosition pos = new java.text.FieldPosition(0);
df.format(dt,dateSB,pos);
appendField(dateSB.toString(),str);
} else if (field==LogField.ENTRYTYPE) {
appendField(LogTypeHelper.fromLogTypeDescription(obj.toString()).logEntryType,str);
} else {
appendField(obj.toString(),str);
}
} else {
// DATA
if (log.hasDatas()) {
appendField(formatData(log.getAdditionalData()),str);
} else {
appendField(null,str);
}
}
}
str.append('\n');
return str.toString();
}
/**
* Append a field to the string (str)
* The field is into double quotes.
* If double quotes exist into the string, they are escaped.
*
* @param fld The field to append
* @param str The string builder where the field is appended
*/
private void appendField(String fld, StringBuilder str) {
if (useDoubleQuotes) {
if (fld==null || fld.length()==0) {
str.append('"');
str.append('"');
return;
}
str.append('"');
str.append(fld.replaceAll("\"","\"\""));
str.append('"');
} else {
if (fld==null || fld.length()==0) {
return;
}
String temp = fld.replace('"','\'');
temp=temp.replace(",","_");
str.append(temp.replace('\n',' '));
}
}
/**
* Format the additional data in a string to be
* appended in the CSV.
* The produced string is not converted in CSV but contains all
* the entries of the additional data
*
* The format is the following:
* [name1 ==> val1] [name2 ==> val2] ....
*
* @param datas The additional data of a log
* @return A string with the additional data
*/
private String formatData(Vector<ILogEntry.AdditionalData> datas) {
StringBuilder temp = new StringBuilder();
boolean first=true;
for (ILogEntry.AdditionalData data: datas) {
if (!first) {
temp.append(' ');
} else {
first=false;
}
temp.append('[');
temp.append(data.name);
temp.append(" ==> ");
temp.append(data.value);
temp.append(']');
}
return temp.toString();
}
}