When I parse the file using Apace POI, the empty rows are getting skipped and a List of String arrays with non-empty rows are being returned.
How can I tell the API, not to skip reading the empty row or columns ?
The code to read is somehow skipping the rows with no data.
XExcelFileReader fileReader = new XExcelFileReader(excelByteArray, sheetFromWhichToRead);
dataFromFile = fileReader.readRows();
is used to read the data from the class XExcelFileReader. The variable dataFromFile is a List of String array.
This is the code to read the data row:
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Pattern;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.xssf.eventusermodel.ReadOnlySharedStringsTable;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.model.StylesTable;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
/**
* This class is responsible for reading the row contents for a given byte array and sheet name
*/
public class XExcelFileReader {
/**
* This is the Row Num of the current row that was read
*/
private int rowNum = 0;
/**
* The OPCPackage is the package used to laod the Input Stream used for only .xlsx Files
*/
private OPCPackage opcPkg;
/**
* These are the String Tables that are read from the Excel File
*/
private ReadOnlySharedStringsTable stringsTable;
/**
* The XML Streaming API will be used
*/
private XMLStreamReader xmlReader;
/** The styles table which has formatting information about cells. */
private StylesTable styles;
/**
* #param excelByteArray the excel byte array
* #param sheetFromWhichToRead the excel sheet from which to read
* #throws Exception the exception
*/
public XExcelFileReader(final byte[] excelByteArray, final String sheetFromWhichToRead) throws Exception {
InputStream excelStream = new ByteArrayInputStream(excelByteArray);
opcPkg = OPCPackage.open(excelStream);
this.stringsTable = new ReadOnlySharedStringsTable(opcPkg);
XSSFReader xssfReader = new XSSFReader(opcPkg);
styles = xssfReader.getStylesTable();
XMLInputFactory factory = XMLInputFactory.newInstance();
XSSFReader.SheetIterator iter = (XSSFReader.SheetIterator) xssfReader.getSheetsData();
InputStream inputStream = null;
while (iter.hasNext()) {
inputStream = iter.next();
String tempSheetName = iter.getSheetName();
if (StringUtils.isNotEmpty(tempSheetName)) {
tempSheetName = tempSheetName.trim();
if (tempSheetName.equals(sheetFromWhichToRead)) {
break;
}
}
}
xmlReader = factory.createXMLStreamReader(inputStream);
while (xmlReader.hasNext()) {
xmlReader.next();
if (xmlReader.isStartElement()) {
if (xmlReader.getLocalName().equals("sheetData")) {
break;
}
}
}
}
/**
* #return rowNum
*/
public final int rowNum() {
return rowNum;
}
/**
* #return List<String[]> List of String array which can hold the content
* #throws XMLStreamException the XMLStreamException
*/
public final List<String[]> readRows() throws XMLStreamException {
String elementName = "row";
List<String[]> dataRows = new ArrayList<String[]>();
while (xmlReader.hasNext()) {
xmlReader.next();
if (xmlReader.isStartElement()) {
if (xmlReader.getLocalName().equals(elementName)) {
rowNum++;
dataRows.add(getDataRow());
// TODO need to see if the batch Size is required
// if (dataRows.size() == batchSize)
// break;
}
}
}
return dataRows;
}
/**
* #return String [] of Row Data
* #throws XMLStreamException the XMLStreamException
*/
private String[] getDataRow() throws XMLStreamException {
List<String> rowValues = new ArrayList<String>();
while (xmlReader.hasNext()) {
xmlReader.next();
if (xmlReader.isStartElement()) {
if (xmlReader.getLocalName().equals("c")) {
CellReference cellReference = new CellReference(xmlReader.getAttributeValue(null, "r"));
// Fill in the possible blank cells!
while (rowValues.size() < cellReference.getCol()) {
rowValues.add("");
}
String cellType = xmlReader.getAttributeValue(null, "t");
String cellStyleStr = xmlReader.getAttributeValue(null, "s");
rowValues.add(getCellValue(cellType, cellStyleStr));
}
} else if (xmlReader.isEndElement() && xmlReader.getLocalName().equals("row")) {
break;
}
}
return rowValues.toArray(new String[rowValues.size()]);
}
/**
* #param cellType the cell type
* #param cellStyleStr the cell style
* #return cell content the cell value
* #throws XMLStreamException the XMLStreamException
*/
private String getCellValue(final String cellType, final String cellStyleStr) throws XMLStreamException {
String value = ""; // by default
while (xmlReader.hasNext()) {
xmlReader.next();
if (xmlReader.isStartElement()) {
if (xmlReader.getLocalName().equals("v")) {
if (cellType != null && cellType.equals("s")) {
int idx = Integer.parseInt(xmlReader.getElementText());
String s = stringsTable.getEntryAt(idx);
return new XSSFRichTextString(s).toString();
}
if (cellStyleStr != null) {
String cellValue = xmlReader.getElementText();
int styleIndex = Integer.parseInt(cellStyleStr);
XSSFCellStyle style = styles.getStyleAt(styleIndex);
short formatIndex = style.getDataFormat();
if (!isValidDouble(cellValue)) {
return cellValue;
}
double doubleVal = Double.valueOf(cellValue);
boolean isValidExcelDate = HSSFDateUtil.isInternalDateFormat(formatIndex);
Date date = null;
if (isValidExcelDate) {
date = HSSFDateUtil.getJavaDate(doubleVal);
String dateStr = dateAsString(date);
return dateStr;
}
if (!(doubleVal == Math.floor(doubleVal))) {
return Double.toString(doubleVal);
}
return cellValue;
}
else {
return xmlReader.getElementText();
}
}
} else if (xmlReader.isEndElement() && xmlReader.getLocalName().equals("c")) {
break;
}
}
return value;
}
/**
* To check whether the incoming value can be used in the Double utility method Double.valueOf() to prevent
* NumberFormatException.
* #param stringVal - String to be validated.
* #return - true if it is a valid String which can be passed into Double.valueOf method. <br/> For more information
* refer- <a>https://docs.oracle.com/javase/7/docs/api/java/lang/Double.html#valueOf(java.lang.String)</a>
*/
private boolean isValidDouble(String stringVal) {
final String Digits = "(\\p{Digit}+)";
final String HexDigits = "(\\p{XDigit}+)";
// an exponent is 'e' or 'E' followed by an optionally
// signed decimal integer.
final String Exp = "[eE][+-]?" + Digits;
final String fpRegex = ("[\\x00-\\x20]*" + // Optional leading "whitespace"
"[+-]?(" + // Optional sign character
"NaN|" + // "NaN" string
"Infinity|" + // "Infinity" string
// A decimal floating-point string representing a finite positive
// number without a leading sign has at most five basic pieces:
// Digits . Digits ExponentPart FloatTypeSuffix
//
// Since this method allows integer-only strings as input
// in addition to strings of floating-point literals, the
// two sub-patterns below are simplifications of the grammar
// productions from the Java Language Specification, 2nd
// edition, section 3.10.2.
// Digits ._opt Digits_opt ExponentPart_opt FloatTypeSuffix_opt
"(((" + Digits + "(\\.)?(" + Digits + "?)(" + Exp + ")?)|" +
// . Digits ExponentPart_opt FloatTypeSuffix_opt
"(\\.(" + Digits + ")(" + Exp + ")?)|" +
// Hexadecimal strings
"((" +
// 0[xX] HexDigits ._opt BinaryExponent FloatTypeSuffix_opt
"(0[xX]" + HexDigits + "(\\.)?)|" +
// 0[xX] HexDigits_opt . HexDigits BinaryExponent FloatTypeSuffix_opt
"(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" +
")[pP][+-]?" + Digits + "))" + "[fFdD]?))" + "[\\x00-\\x20]*");// Optional trailing "whitespace"
if (Pattern.matches(fpRegex, stringVal))
return true;
else {
return false;
}
}
/**
* Date as string.
* #param date the date
* #return the string
*/
public static String dateAsString(final Date date) {
String dateAsString = null;
if (date != null) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
dateAsString = sdf.format(date);
}
return dateAsString;
}
#Override
protected final void finalize() throws Throwable {
if (opcPkg != null) {
opcPkg.close();
}
super.finalize();
}
}
I want the Empty row also to be part of the List of String array. Just that the String array will be empty.
If it is in the Excel like:
First Row
<< No Data >>
Third Row
then the List should be
dataFromFile
0 ->[First Row]
1 ->[]
2 ->[Third Row]
Here is what the Busy Developers Guide on the POI site says about reading sheets with missing rows:
for (int rowNum = rowStart; rowNum < rowEnd; rowNum++) {
Row r = sheet.getRow(rowNum);
if (r == null) {
// This whole row is empty
// Handle it as needed
continue;
}
int lastColumn = Math.max(r.getLastCellNum(), MY_MINIMUM_COLUMN_COUNT);
for (int cn = 0; cn < lastColumn; cn++) {
Cell c = r.getCell(cn, Row.RETURN_BLANK_AS_NULL);
if (c == null) {
// The spreadsheet is empty in this cell
} else {
// Do something useful with the cell's contents
}
}
}
You really can get a lot of your answers there. You shouldn't ever have to parse the XML directly. Even things that are missing from the high level usermodel usually have a way to get at it through the CT classes generated by XMLBeans.
Related
I am new to this and i need to split Srt(subtitle file) into multiple chunks.
For example: if i have subtitle file of a video(60 minutes). Then the subtitle file should split into 6 subtitle files having each subtitle file of 10 minutes.
i.e 6 X 10 = 60 Minutes
Need to divide into 6 chunks irrespective of minutes.
Using these each subtitle time/duration, i have to split the video into same chunks.
I am trying this code, can u please help me out how can i calculate the time and divide into chunks,
I am able to achieve how many minutes of chuck i needed.But stuck in how to read upto that chunck minutes from source file and create a new file .Then how to start the next chunk from the next 10 minutes from the source file.
import org.apache.commons.io.IOUtils;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.stream.Stream;
/**
* The class SyncSRTSubtitles reads a subtitles .SRT file and offsets all the
* timestamps with the same specific value in msec.
*
* The format of the .SRT file is like this:
*
* 123
* 00:11:23,456 --> 00:11:25,234
* subtitle #123 text here
*
*
* #author Sorinel CRISTESCU
*/
public class SyncSRTSubtitles {
/**
* Entry point in the program.
*
* #param args
* #throws IOException
*/
public static void main(String[] args) throws IOException {
/* INPUT: offset value: negative = less (-) ... positive = more (+). */
long delta = (22 * 1000L + 000); /* msec */
/* INPUT: source & destination files */
String srcFileNm = "/Users/meh/Desktop/avatar.srt";
String destFileNm = "/Users/meh/Desktop/avatar1.srt";
/* offset algorithm: START */
File outFile = new File(destFileNm);
outFile.createNewFile();
FileWriter ofstream = new FileWriter(outFile);
BufferedWriter out = new BufferedWriter(ofstream);
/* Open the file that is the first command line parameter */
FileInputStream fstream = new FileInputStream(srcFileNm);
DataInputStream in = new DataInputStream(fstream);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String strLine;
// List<String> doc = IOUtils.readLines(in, StandardCharsets.UTF_8);
String strEnd = null;
long diff = 0;
String line;
String startTS1;
try (Stream<String> lines = Files.lines(Paths.get(srcFileNm))) {
line = lines.skip(1).findFirst().get();
String[] atoms = line.split(" --> ");
startTS1 = atoms[0];
}
System.out.println("bolo:" +line);
System.out.println("startTS1:" +startTS1);
String startTS = null;
String endTS = null;
/* Read File Line By Line */
while ((strLine = br.readLine()) != null) {
String[] atoms = strLine.split(" --> ");
if (atoms.length == 1) {
//out.write(strLine + "\n");
}
else {
startTS = atoms[0];
endTS = atoms[1];
// out.write(offsetTime(startTS, delta) + " --> "
// + offsetTime(endTS, delta) + "\n");
strEnd = endTS;
}
}
try {
SimpleDateFormat dateFormat = new SimpleDateFormat("hh:mm:ss");
Date parsedendDate = dateFormat.parse(strEnd);
Date parsedStartDate = dateFormat.parse(startTS1);
diff = parsedendDate.getTime() - parsedStartDate.getTime();
} catch(Exception e) { //this generic but you can control another types of exception
// look the origin of excption
}
System.out.println("strEnd");
System.out.println(strEnd);
/* Close the input streams */
in.close();
out.close();
System.out.println(diff);
long diff1 =diff/6;
System.out.println(diff1);
long diff2= (diff1*6);
System.out.println(diff2);
System.out.println((diff / 3600000) + " hour/s " + (diff % 3600000) / 60000 + " minutes");
System.out.println((diff1 / 3600000) + " hour/s " + (diff1 % 3600000) / 60000 + " minutes");
System.out.println((diff2 / 3600000) + " hour/s " + (diff2 % 3600000) / 60000 + " minutes");
/* offset algorithm: END */
System.out.println("DONE! Check the rsult oin the file: " + destFileNm);
}
/**
* Computes the timestamp offset.
*
* #param ts
* String value of the timestamp in format: "hh:MM:ss,mmm"
* #param delta
* long value of the offset in msec (positive or negative).
* #return String with the new timestamp representation.
*/
private static String offsetTime(String ts, long delta) {
long tsMsec = 0;
String atoms[] = ts.split("\\,");
if (atoms.length == 2) {
tsMsec += Integer.parseInt(atoms[1]);
}
atoms = atoms[0].split(":");
tsMsec += Integer.parseInt(atoms[2]) * 1000L; /* seconds */
tsMsec += Integer.parseInt(atoms[1]) * 60000L; /* minutes */
tsMsec += Integer.parseInt(atoms[0]) * 3600000L; /* hours */
tsMsec += delta; /* here we do the offset. */
long h = tsMsec / 3600000L;
System.out.println(h);
String result = get2digit(h, 2) + ":";
System.out.println(result);
long r = tsMsec % 3600000L;
System.out.println(r);
long m = r / 60000L;
System.out.println(m);
result += get2digit(m, 2) + ":";
System.out.println(result);
r = r % 60000L;
System.out.println(r);
long s = r / 1000L;
result += get2digit(s, 2) + ",";
result += get2digit(r % 1000L, 3);
System.out.println(result);
return result;
}
/**
* Gets the string representation of the number, adding the prefix '0' to
* have the required length.
*
* #param n
* long number to convert to string.
* #param digits
* int number of digits required.
* #return String with the required length string (3 for digits = 3 -->
* "003")
*/
private static String get2digit(long n, int digits) {
String result = "" + n;
while (result.length() < digits) {
result = "0" + result;
}
return result;
}
}
Please suggest me how can i achieve this?
You will need to parse the file twice:
once to read the last end time
second time to process all lines and
generate output files.
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import org.apache.commons.io.FileUtils;
public class SplitSRTFiles {
/**
* Splits a SRT file in multiple files each containing an equal time duration.
* #param args
* [0] number of wanted chunks
* [1] source file name
* #throws IOException
*/
public static void main(String[] args) throws IOException {
int nrOfChunks = Integer.parseInt(args[0]);
File srtFile = new File(args[1]);
System.out.println("Splitting "+srtFile.getAbsolutePath()+" into "+nrOfChunks+" files.");
List<String> srcLines = FileUtils.readLines(srtFile);
long fileEndTime = lastEndTime(srcLines);
long msecsPerChunkFile = fileEndTime / nrOfChunks;
int destFileCounter = 1;
String[] fileNameParts = srtFile.getName().split("\\.");
File outFile = new File(fileNameParts[0] + destFileCounter + "." + fileNameParts[1]);
System.out.println("Writing to "+outFile.getAbsolutePath());
outFile.createNewFile();
FileWriter ofstream = new FileWriter(outFile);
BufferedWriter out = new BufferedWriter(ofstream);
for (String line : srcLines) {
String[] atoms = line.split(" --> ");
if (atoms.length > 1) {
long startTS = toMSec(atoms[0]);
// check if start time of this subtitle is after the current
// chunk
if (startTS > msecsPerChunkFile * destFileCounter) {
// close existing file ...
out.close();
ofstream.close();
// ... and start a new file
destFileCounter++;
outFile = new File(srtFile.getParent(), fileNameParts[0] + destFileCounter + "." + fileNameParts[1]);
System.out.println("Writing to "+outFile.getAbsolutePath());
outFile.createNewFile();
ofstream = new FileWriter(outFile);
out = new BufferedWriter(ofstream);
}
}
out.write(line + "/n");
}
out.close();
ofstream.close();
System.out.println("Done.");
}
/**
* Calculates the time in msec of the end time of the last subtitle of the
* file
*
* #param lines
* read from file
* #return end time in milliseconds of the last subtitle
*/
public static long lastEndTime(List lines) throws IOException {
String endTS = null;
for (String line : lines) {
String[] atoms = line.split(" --> ");
if (atoms.length > 1) {
endTS = atoms[1];
}
}
return endTS == null ? 0L : toMSec(endTS);
}
public static long toMSec(String time) {
long tsMsec = 0;
String atoms[] = time.split("\\,");
if (atoms.length == 2) {
tsMsec += Integer.parseInt(atoms[1]);
}
atoms = atoms[0].split(":");
tsMsec += Integer.parseInt(atoms[2]) * 1000L; /* seconds */
tsMsec += Integer.parseInt(atoms[1]) * 60000L; /* minutes */
tsMsec += Integer.parseInt(atoms[0]) * 3600000L; /* hours */
return tsMsec;
}
}
I have figured out a way to divide the file into chunks,
public static void main(String args[]) throws IOException {
String FilePath = "/Users/meh/Desktop/escapeplan.srt";
FileInputStream fin = new FileInputStream(FilePath);
System.out.println("size: " +fin.getChannel().size());
long abc = 0l;
abc = (fin.getChannel().size())/3;
System.out.println("6: " +abc);
System.out.println("abc: " +abc);
//FilePath = args[1];
File filename = new File(FilePath);
long splitFileSize = 0,bytefileSize=0;
if (filename.exists()) {
try {
//bytefileSize = Long.parseLong(args[2]);
splitFileSize = abc;
Splitme spObj = new Splitme();
spObj.split(FilePath, (long) splitFileSize);
spObj = null;
} catch (Exception e) {
e.printStackTrace();
}
} else {
System.out.println("File Not Found....");
}
}
public void split(String FilePath, long splitlen) {
long leninfile = 0, leng = 0;
int count = 1, data;
try {
File filename = new File(FilePath);
InputStream infile = new BufferedInputStream(new FileInputStream(filename));
data = infile.read();
System.out.println("data");
System.out.println(data);
while (data != -1) {
filename = new File("/Users/meh/Documents/srt" + count + ".srt");
//RandomAccessFile outfile = new RandomAccessFile(filename, "rw");
OutputStream outfile = new BufferedOutputStream(new FileOutputStream(filename));
while (data != -1 && leng < splitlen) {
outfile.write(data);
leng++;
data = infile.read();
}
leninfile += leng;
leng = 0;
outfile.close();
changeTimeStamp(filename, count);
count++;
}
} catch (Exception e) {
e.printStackTrace();
}
}
i have attached screen shot of the resultant CSV file
I am trying to write the result of the dql queries into a separate CSV file using dfc coding. but the result of the first record is printing twice in the resultant sheet.
public class ChkGroupExistence {
/**
* #param args
*/
private static Properties queryProp;
//private static Properties configProp;
IDfSession sess = null;
public ChkGroupExistence() {
System.out.println("Loading Properties..");
LoadProps loadProp = LoadProps.getInstance();
queryProp = loadProp.getQueryProp();
//configProp = loadProp.getConfigProp();
List<String> proj_list = new ArrayList<String>();
List<String> grp_list = new ArrayList<String>();
List<String> acl_list = new ArrayList<String>();
HashMap<String, String> projList_Map = new HashMap<String, String>();
IDfCollection projId_coll = null;
IDfCollection grp_coll = null;
//IDfCollection chk_coll = null;
//IDfCollection acl_coll = null;
String grpqry = null;
String chkqry = null;
//String getACLQuery = null;
int j=0;
CreateSession ifcDocsDfSession = new CreateSession();
try {
sess = ifcDocsDfSession.getSession();
DfLogger.info(this, "Session Created ::" + sess.getSessionId(),
null, null);
} catch (DfException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String qry = queryProp
.getProperty(IdocsConstants.PROJECT_ID);
try {
CSVWriter csvwriter=new CSVWriter(new FileWriter(new File("C:\\WBG\\IFCTools\\cs_log.csv")));
projId_coll = Util.executeQuery(sess, qry, IDfQuery.READ_QUERY);
while (projId_coll.next()) {
proj_list.add(projId_coll.getString("project_id"));
}
System.out.println("List of Project ::"+proj_list.size());
String tempQuery=queryProp.getProperty(IdocsConstants.P_GROUP_EXIST);
//String tempQuery1=queryProp.getProperty(IdocsConstants.P_GROUP_VERIFY);
//String tempQuery2 = queryProp.getProperty(IdocsConstants.P_GETACL);
List<String[]> csvList=new ArrayList<String[]>();
List<String[]> titleList = new ArrayList<String[]>();
String[] projList;
String[] titleString;
titleString = new String[3];
titleString[0]="ProjectId/Institutionnbr";
titleString[1]="GroupName";
titleString[2]="ACL Name";
titleList.add(titleString);
csvwriter.writeAll(titleList);
for(int i = 0; i <proj_list.size();i++ ) {
//grpqry = tempQuery+proj_list.get(i) + "_ed_off_grp'" ;
grpqry = MessageFormat.format(tempQuery,proj_list.get(i));
//chkqry = queryProp.getProperty(IdocsConstants.P_GROUP_VERIFY);
//System.out.println(grpqry);
//getACLQuery = MessageFormat.format(tempQuery2, proj_list.get(i));
//System.out.println(getACLQuery);
//System.out.println("grp_coll query is executing....");
grp_coll = Util.executeQuery(sess, grpqry, IDfQuery.READ_QUERY);
//System.out.println("verification query is executing.....");
//chk_coll = Util.executeQuery(sess, chkqry, IDfQuery.READ_QUERY);
//acl_coll = Util.executeQuery(sess, getACLQuery, IDfQuery.READ_QUERY);
if (grp_coll!=null && grp_coll.next()) {
String grpName = grp_coll.getString("group_name");
grp_list.add(grpName);
System.out.println("Got group for "+proj_list.get(i)+" :: "+grpName);
projList=new String[3];
projList[0]=proj_list.get(i);
projList[1]=grpName;
//System.out.println(grpName);
projList_Map.put(proj_list.get(i),grp_list.get(j));
j++;
System.out.println(projList_Map.size());
if(chkqry == null){
//System.out.println("group names are adding to the list.....");
//grp_list.add(grpName);
String acl_name = queryProp.getProperty(IdocsConstants.P_GETACL);
acl_list.add(acl_name);
projList[2]=acl_name;
}
csvList.add(projList);
csvwriter.writeAll(csvList);
}
}
System.out.println("Project List is loading....");
Set<String> keySet = projList_Map.keySet();
System.out.println(grp_list);
System.out.println(acl_list);
for(String set : keySet) {
System.out.println(set + " : " +projList_Map.get(set));
}
csvwriter.close();
} catch (DfException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (IndexOutOfBoundsException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String args[])
{
ChkGroupExistence chkexist = new ChkGroupExistence();
}
}
CSVWriter.java
public class CSVWriter {
/**
* Default Delimiter will be used if not given.
*/
private static final char DEF_DELIM = ',';
/**
* Default Quote Character will be used if not given.
*/
private static final char DEF_QUOTE_CHAR = '"';
/**
* Default End of Line Character.
*/
private static final char DEFAULT_EOL = '\n';
/**
* Contains the Delimtter.
*/
private char delimiter;
/**
* Contains the Quote character.
*/
private char quotechar;
/**
* String contains the End of the line character.
*/
private char cLineEnd;
/**
* Instance Variable to hold Write Object.
*/
private Writer rawWriter;
/**
* Instance variable to hold PrintWriter object
*/
private PrintWriter pw;
/**
* Constructor to take File Writer as Input.
*
* #param writer
* File Writer
*/
public CSVWriter(Writer writer) {
this(writer, DEF_DELIM);
}
/**
* Constructor to take File Writer and Delimiter as Input.
*
* #param writer
* File Writer
* #param delim
* Delimiter
*/
public CSVWriter(Writer writer, char delim) {
this(writer, delim, DEF_QUOTE_CHAR);
}
/**
* Constructor to take File Writer, Delimiter and Quote Character as Input.
*
* #param writer
* File Writer
* #param delim
* Delimiter
* #param quote
* Quote Character
*/
public CSVWriter(Writer writer, char delim, char quote) {
this(writer, delim, quote, DEFAULT_EOL);
}
/**
* Constructor to take File Writer, Delimiter, Quote Character and End of
* Line feed as Input.
*
* #param writer
* File Writer
* #param delim
* Delimiter
* #param quote
* #param sEOL
*/
public CSVWriter(Writer writer, char delim, char quote, char sEOL) {
rawWriter = writer;
pw = new PrintWriter(writer);
delimiter = delim;
quotechar = quote;
cLineEnd = sEOL;
}
/**
* Method takes List as input and writes values into the CSV file.
*
* #param list
* List of Cell values.
*/
public void writeAll(List list) {
String sRow[];
for (Iterator iter = list.iterator(); iter.hasNext(); writeNext(sRow)) {
sRow = (String[]) iter.next();
}
}
/**
* Method that takes String[] as input and writes each and every cell.
*
* #param sRow
* String[]
*/
private void writeNext(String sRow[]) {
StringBuffer stringbuffer = new StringBuffer();
for (int i = 0; i < sRow.length; i++) {
if (i != 0) {
stringbuffer.append(delimiter);
}
String s = sRow[i];
if (s == null) {
continue;
}
if (quotechar != 0) {
stringbuffer.append(quotechar);
}
for (int j = 0; j < s.length(); j++) {
char c = s.charAt(j);
if (c == quotechar) {
stringbuffer.append(DEF_QUOTE_CHAR).append(c);
continue;
}
if (c == DEF_QUOTE_CHAR) {
stringbuffer.append(DEF_QUOTE_CHAR).append(c);
} else {
stringbuffer.append(c);
}
}
if (quotechar != 0) {
stringbuffer.append(quotechar);
}
}
stringbuffer.append(cLineEnd);
pw.write(stringbuffer.toString());
}
/**
* Method that closed the Print Writer. Only when this method is called, the
* CSV file will be saved.
*
* #throws IOException
*/
public void close() throws IOException {
pw.flush();
pw.close();
rawWriter.close();
}
Probably because you're including a repeating attribute in your query.
Tip: I tend to include the object ID for reference and debugging when I do this.
You can also insert a "DISTINCT" keyword into your query when including these repeating attributes.
The reason they act like this is the nature of the data model: all repeating attributes are stored in the same table - so when one of these attributes contains multiple values a query could return multiple rows when querying another repeating attribute. In this case a SELECT DISTINCT would do the trick.
You can also play around with the DQL hint ENABLE(ROW_BASED) if you are joining repeating attribute with single value attributes.
Happy coding!
I am reading an Excel file using POI's XSSF and SAX (Event API). The Excel sheet has thousands of rows so this is the only way that I have found to have good performance. Now I would like to read Excel file from one particularly row, for example row 6 because these Excel files are updated every day and I store the last row that I have already stored so I can read only the new rows.
How can I start from one row?
private void getExcelField(AcquisitionForm acquisitionForm) throws ExcelReadException, IOException{
InputStream stream=null;
OPCPackage p=null;
try{
p = OPCPackage.open(acquisitionForm.getDatatablePath(), PackageAccess.READ);
ReadOnlySharedStringsTable strings = new ReadOnlySharedStringsTable(p);
XSSFReader xssfReader = new XSSFReader(p);
StylesTable styles = xssfReader.getStylesTable();
XSSFReader.SheetIterator iter = (XSSFReader.SheetIterator) xssfReader.getSheetsData();
int index = 0;
//Test with one sheet
iter.hasNext();
//(iter.hasNext()) {
stream = iter.next();
String sheetName = iter.getSheetName();
processSheet(styles, strings, stream, acquisitionForm);
stream.close();
//++index;
//}
p.close();
}catch(Exception e){
throw new ExcelReadException("An error occured during excel file reading ", e);
}finally{
if (stream!=null)
stream.close();
if (p!=null)
p.close();
//++index;
// }
}
}
/**
* Parses and shows the content of one sheet
* using the specified styles and shared-strings tables.
* #param styles
* #param strings
* #param sheetInputStream
* #throws ExcelReadException
*/
private void processSheet(StylesTable styles, ReadOnlySharedStringsTable strings, InputStream sheetInputStream, AcquisitionForm acquisitionForm) throws Exception {
InputSource sheetSource = new InputSource(sheetInputStream);
SAXParserFactory saxFactory = SAXParserFactory.newInstance();
SAXParser saxParser = saxFactory.newSAXParser();
XMLReader sheetParser = saxParser.getXMLReader();
//ContentHandler handler = new MyXSSFSheetHandler(styles, strings);
MyXSSFSheetHandler handler = new MyXSSFSheetHandler(styles, strings,databaseAcquisitionServices, acquisitionForm);
sheetParser.setContentHandler(handler);
sheetParser.parse(sheetSource);
}
MyXSSFSheetHandler
public MyXSSFSheetHandler(StylesTable styles, ReadOnlySharedStringsTable strings,DatabaseAcquisitionServices databaseAcquisitionServices, AcquisitionForm acquisitionForm, int sheetIndex) {
this.stylesTable = styles;
this.sharedStringsTable = strings;
this.formatter = new DataFormatter();
this.value = new StringBuffer();
this.nextDataType = XssfDataType.NUMBER;
this.databaseAcquisitionServices=databaseAcquisitionServices;
this.acquisitionForm=acquisitionForm;
this.sheetIndex = sheetIndex;
}
/**
*
*/
public void startElement(String uri, String localName, String name,
Attributes attributes) throws SAXException {
if ("inlineStr".equals(name) || "v".equals(name)) {
vIsOpen = true;
// Clear contents cache
value.setLength(0);
}
// c => cell
else if ("c".equals(name)) {
// Get the cell reference
cellCoordinate = attributes.getValue("r");
int firstDigit = -1;
for (int c = 0; c < cellCoordinate.length(); ++c) {
if (Character.isDigit(cellCoordinate.charAt(c))) {
firstDigit = c;
break;
}
}
thisColumn = nameToColumn(cellCoordinate.substring(0, firstDigit));
// Set up defaults.
this.nextDataType = XssfDataType.NUMBER;
this.formatIndex = -1;
this.formatString = null;
String cellType = attributes.getValue("t");
String cellStyleStr = attributes.getValue("s");
if ("b".equals(cellType)) {
nextDataType = XssfDataType.BOOL;
} else if ("e".equals(cellType)) {
nextDataType = XssfDataType.ERROR;
} else if ("inlineStr".equals(cellType)) {
nextDataType = XssfDataType.INLINESTR;
} else if ("s".equals(cellType)) {
nextDataType = XssfDataType.SSTINDEX;
} else if ("str".equals(cellType)) {
nextDataType = XssfDataType.FORMULA;
} else if (cellStyleStr != null) {
// It's a number, but almost certainly one
// with a special style or format
int styleIndex = Integer.parseInt(cellStyleStr);
XSSFCellStyle style = stylesTable.getStyleAt(styleIndex);
this.formatIndex = style.getDataFormat();
this.formatString = style.getDataFormatString();
if (this.formatString == null) {
this.formatString = BuiltinFormats.getBuiltinFormat(this.formatIndex);
}
}
}
}
/*
* (non-Javadoc)
* #see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
*/
public void endElement(String uri, String localName, String name)
throws SAXException {
String cellValue = null;
//String thisStr = null;
// v => contents of a cell
if ("v".equals(name)) {
// Process the value contents as required.
// Do now, as characters() may be called more than once
switch (nextDataType) {
case BOOL:
char first = value.charAt(0);
//thisStr = first == '0' ? "FALSE" : "TRUE";
//cellValue= new Boolean(first =='0' ? false: true);
cellValue=first == '0' ? "false" : "true";
break;
case ERROR:
//thisStr = "\"ERROR:" + value.toString() + '"';
cellValue=new String(value.toString());
break;
case FORMULA:
// A formula could result in a string value,
// so always add double-quote characters.
//thisStr = '"' + value.toString() + '"';
cellValue=new String(value.toString());
break;
case INLINESTR:
XSSFRichTextString rtsi = new XSSFRichTextString(value.toString());
//thisStr = '"' + rtsi.toString() + '"';
cellValue=new String(rtsi.toString());
break;
case SSTINDEX:
String sstIndex = value.toString();
try {
int idx = Integer.parseInt(sstIndex);
XSSFRichTextString rtss = new XSSFRichTextString(sharedStringsTable.getEntryAt(idx));
// thisStr = '"' + rtss.toString() + '"';
cellValue=new String(rtss.toString());
}
catch (NumberFormatException ex) {
System.out.println("Failed to parse SST index '" + sstIndex + "': " + ex.toString());
}
break;
case NUMBER:
String n = value.toString();
if (this.formatString != null && n.length() > 0){
cellValue = formatter.formatRawCellContents(Double.parseDouble(n), this.formatIndex, this.formatString);
//cellValue=new Double(Double.parseDouble(n));
}
else{
//thisStr = n;
cellValue=new String(n);
}
break;
default:
cellValue="";
//thisStr = "(TODO: Unexpected type: " + nextDataType + ")";
break;
}
// Output after we've seen the string contents
// Emit commas for any fields that were missing on this row
if (lastColumnNumber == -1) {
lastColumnNumber = 0;
}
// for (int i = lastColumnNumber; i < thisColumn; ++i){
// System.out.print(',');
// }
// Might be the empty string.
rowValues.put(cellCoordinate,cellValue);
//System.out.print(cellValue);
// Update column
if (thisColumn > -1)
lastColumnNumber = thisColumn;
} else if ("row".equals(name)) {
// We're onto a new row
//I have to pass new HashMap because otherwise all the map into archiveAcquisition have the same values
databaseAcquisitionServices.archiveAcquisition(new TreeMap<>(rowValues), rowCounter, acquisitionForm, sheetIndex);
//Clear the structure used to store row data
rowValues.clear();
rowCounter++;
//System.out.println();
lastColumnNumber = -1;
}
}
/**
* Captures characters only if a suitable element is open.
* Originally was just "v"; extended for inlineStr also.
*/
public void characters(char[] ch, int start, int length)
throws SAXException {
if (vIsOpen)
value.append(ch, start, length);
}
/**
* Converts an Excel column name like "C" to a zero-based index.
*
* #param name
* #return Index corresponding to the specified name
*/
private int nameToColumn(String name) {
int column = -1;
for (int i = 0; i < name.length(); ++i) {
int c = name.charAt(i);
column = (column + 1) * 26 + c - 'A';
}
return column;
}
You will likely need to count the number of rows that you see in a class derived from SheetContentsHandler, I don't think you need to have a separate XSSFSheetHAndler, I would rather use the default one and only have a dervied SheetContentsHandler which has callback-methods for all the things that you are interested in, e.g. startRow, endRow, cell.
You can take a look at the XLSX2CSV sample for an example of how streaming reading of XLSX files can be done, especially how the class SheetToCSV is used to get calls for each row/cell, you could e.g. use something like if (currentRow < startRow) { return } in the method cell() to skip rows.
The cell-method is called with a parameter String cellReference which can be used to retrieve the row/cell coordinates via new CellReference(cellReference).getCol() and new CellReference(cellReference).getRow().
I am reading an Excel sheet using POI's XSSF and SAX (Event API). The Excel sheet has thousands of rows of user information like user name, email, address, age, department etc.
I need to read each row from Excel, convert it into a User object and add this User object to a List of User objects.
I can read the Excel sheet successfully, but I am not sure at what point while reading I should create an instance of the User object and populate it with the data from the Excel sheet.
Below is my entire working code.
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackageAccess;
import org.apache.poi.ss.usermodel.BuiltinFormats;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.xssf.eventusermodel.ReadOnlySharedStringsTable;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.model.StylesTable;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
public class ExcelSheetParser {
enum xssfDataType {
BOOL, ERROR, FORMULA, INLINESTR, SSTINDEX, NUMBER,
}
int countrows = 0;
class XSSFSheetHandler extends DefaultHandler {
/**
* Table with styles
*/
private StylesTable stylesTable;
/**
* Table with unique strings
*/
private ReadOnlySharedStringsTable sharedStringsTable;
/**
* Destination for data
*/
private final PrintStream output;
private List<?> list = new ArrayList();
private Class clazz;
/**
* Number of columns to read starting with leftmost
*/
private final int minColumnCount;
// Set when V start element is seen
private boolean vIsOpen;
// Set when cell start element is seen;
// used when cell close element is seen.
private xssfDataType nextDataType;
// Used to format numeric cell values.
private short formatIndex;
private String formatString;
private final DataFormatter formatter;
private int thisColumn = -1;
// The last column printed to the output stream
private int lastColumnNumber = -1;
// Gathers characters as they are seen.
private StringBuffer value;
/**
* Accepts objects needed while parsing.
*
* #param styles
* Table of styles
* #param strings
* Table of shared strings
* #param cols
* Minimum number of columns to show
* #param target
* Sink for output
*/
public XSSFSheetHandler(StylesTable styles,
ReadOnlySharedStringsTable strings, int cols, PrintStream target, Class clazz) {
this.stylesTable = styles;
this.sharedStringsTable = strings;
this.minColumnCount = cols;
this.output = target;
this.value = new StringBuffer();
this.nextDataType = xssfDataType.NUMBER;
this.formatter = new DataFormatter();
this.clazz = clazz;
}
public void startElement(String uri, String localName, String name,
Attributes attributes) throws SAXException {
if ("inlineStr".equals(name) || "v".equals(name)) {
vIsOpen = true;
// Clear contents cache
value.setLength(0);
}
// c => cell
else if ("c".equals(name)) {
// Get the cell reference
String r = attributes.getValue("r");
int firstDigit = -1;
for (int c = 0; c < r.length(); ++c) {
if (Character.isDigit(r.charAt(c))) {
firstDigit = c;
break;
}
}
thisColumn = nameToColumn(r.substring(0, firstDigit));
// Set up defaults.
this.nextDataType = xssfDataType.NUMBER;
this.formatIndex = -1;
this.formatString = null;
String cellType = attributes.getValue("t");
String cellStyleStr = attributes.getValue("s");
if ("b".equals(cellType))
nextDataType = xssfDataType.BOOL;
else if ("e".equals(cellType))
nextDataType = xssfDataType.ERROR;
else if ("inlineStr".equals(cellType))
nextDataType = xssfDataType.INLINESTR;
else if ("s".equals(cellType))
nextDataType = xssfDataType.SSTINDEX;
else if ("str".equals(cellType))
nextDataType = xssfDataType.FORMULA;
else if (cellStyleStr != null) {
// It's a number, but almost certainly one
// with a special style or format
int styleIndex = Integer.parseInt(cellStyleStr);
XSSFCellStyle style = stylesTable.getStyleAt(styleIndex);
this.formatIndex = style.getDataFormat();
this.formatString = style.getDataFormatString();
if (this.formatString == null)
this.formatString = BuiltinFormats
.getBuiltinFormat(this.formatIndex);
}
}
}
public void endElement(String uri, String localName, String name)
throws SAXException {
String thisStr = null;
// v => contents of a cell
if ("v".equals(name)) {
// Process the value contents as required.
// Do now, as characters() may be called more than once
switch (nextDataType) {
case BOOL:
char first = value.charAt(0);
thisStr = first == '0' ? "FALSE" : "TRUE";
break;
case ERROR:
thisStr = "\"ERROR:" + value.toString() + '"';
break;
case FORMULA:
// A formula could result in a string value,
// so always add double-quote characters.
thisStr = '"' + value.toString() + '"';
break;
case INLINESTR:
// TODO: have seen an example of this, so it's untested.
XSSFRichTextString rtsi = new XSSFRichTextString(value
.toString());
thisStr = '"' + rtsi.toString() + '"';
break;
case SSTINDEX:
String sstIndex = value.toString();
try {
int idx = Integer.parseInt(sstIndex);
XSSFRichTextString rtss = new XSSFRichTextString(
sharedStringsTable.getEntryAt(idx));
thisStr = '"' + rtss.toString() + '"';
} catch (NumberFormatException ex) {
output.println("Failed to parse SST index '" + sstIndex
+ "': " + ex.toString());
}
break;
case NUMBER:
String n = value.toString();
if (this.formatString != null)
thisStr = formatter.formatRawCellContents(Double
.parseDouble(n), this.formatIndex,
this.formatString);
else
thisStr = n;
break;
default:
thisStr = "(TODO: Unexpected type: " + nextDataType + ")";
break;
}
// Output after we've seen the string contents
// Emit commas for any fields that were missing on this row
if (lastColumnNumber == -1) {
lastColumnNumber = 0;
}
for (int i = lastColumnNumber; i < thisColumn; ++i)
output.print(',');
// Might be the empty string.
output.print(thisColumn +" : "+thisStr);
// Update column
if (thisColumn > -1)
lastColumnNumber = thisColumn;
} else if ("row".equals(name)) {
// Print out any missing commas if needed
if (minColumns > 0) {
// Columns are 0 based
if (lastColumnNumber == -1) {
lastColumnNumber = 0;
}
for (int i = lastColumnNumber; i < (this.minColumnCount); i++) {
output.print(',');
}
}
// We're onto a new row
output.println();
output.println(countrows++);
lastColumnNumber = -1;
}
}
/**
* Captures characters only if a suitable element is open. Originally
* was just "v"; extended for inlineStr also.
*/
public void characters(char[] ch, int start, int length)
throws SAXException {
if (vIsOpen)
value.append(ch, start, length);
}
/**
* Converts an Excel column name like "C" to a zero-based index.
*
* #param name
* #return Index corresponding to the specified name
*/
private int nameToColumn(String name) {
int column = -1;
for (int i = 0; i < name.length(); ++i) {
int c = name.charAt(i);
column = (column + 1) * 26 + c - 'A';
}
return column;
}
}
// /////////////////////////////////////
private OPCPackage xlsxPackage;
private int minColumns;
private PrintStream output;
private Class clazz;
/**
* Creates a new XLSX -> CSV converter
*
* #param pkg
* The XLSX package to process
* #param output
* The PrintStream to output the CSV to
* #param minColumns
* The minimum number of columns to output, or -1 for no minimum
*/
public ExcelSheetParser(OPCPackage pkg, PrintStream output, int minColumns, Class clazz) {
this.xlsxPackage = pkg;
this.output = output;
this.minColumns = minColumns;
this.clazz = clazz;
}
/**
* Parses and shows the content of one sheet using the specified styles and
* shared-strings tables.
*
* #param styles
* #param strings
* #param sheetInputStream
*/
public void processSheet(StylesTable styles,
ReadOnlySharedStringsTable strings, InputStream sheetInputStream)
throws IOException, ParserConfigurationException, SAXException {
InputSource sheetSource = new InputSource(sheetInputStream);
SAXParserFactory saxFactory = SAXParserFactory.newInstance();
SAXParser saxParser = saxFactory.newSAXParser();
XMLReader sheetParser = saxParser.getXMLReader();
ContentHandler handler = new XSSFSheetHandler(styles, strings,
this.minColumns, this.output, this.clazz);
sheetParser.setContentHandler(handler);
sheetParser.parse(sheetSource);
}
/**
* Initiates the processing of the XLS workbook file to CSV.
*
* #throws IOException
* #throws OpenXML4JException
* #throws ParserConfigurationException
* #throws SAXException
*/
public void process() throws IOException, OpenXML4JException,
ParserConfigurationException, SAXException {
ReadOnlySharedStringsTable strings = new ReadOnlySharedStringsTable(
this.xlsxPackage);
XSSFReader xssfReader = new XSSFReader(this.xlsxPackage);
StylesTable styles = xssfReader.getStylesTable();
XSSFReader.SheetIterator iter = (XSSFReader.SheetIterator) xssfReader
.getSheetsData();
int index = 0;
while (iter.hasNext()) {
InputStream stream = iter.next();
String sheetName = iter.getSheetName();
this.output.println(sheetName + " [index=" + index + "]:");
processSheet(styles, strings, stream);
stream.close();
++index;
}
}
}
What I'd probably do is start building the User object when the row starts. As you hit the cells in the row, you populate your User object. When the row ends, validate the User object, and if it's fine add it then. Because you're doing SAX parsing, you'll get the start and events for all of these, so you can attach your logic there.
I'd suggest you take a look at XLSX2CSV in the Apache POI Examples. It shows how to go about handling the different kinds of cell contents (which you'll need for populating your user object), how to do something when you reach the end of the row, as well as handling missing cells etc.
I think you can create a user object at following location in your code:
// We're onto a new row
output.println();
// Convert output to a new user object
// ....
// ....
First of all where you are saving value in thisStr variable, if this is a valid value then put this value in Map.
You should create USer object in endElement() method in
else if ("row".equals(name)) {
// use map create USER object here
}
and You can add Users object in global list and if you want to persist it then you can persist it sheet by sheet OR all data at a time.
while (iter.hasNext()) {
InputStream stream = iter.next();
String sheetName = iter.getSheetName();
this.output.println(sheetName + " [index=" + index + "]:");
processSheet(styles, strings, stream);
stream.close();
++index;
//for persisting USERS data sheet by sheet write your code here.........
}
// for persisting complete data of all sheets write your code here...
This is working for me.
For my java project, i am using the java logging api. I want to log everything using a property file.
Before using this file (log.properties), I configured my onwn formatter in the java code. (see below)
Now I want to configure my own fomatter in the propertie file, instead of the java code.
does someone know how to do that ?
Formatter formatter = new Formatter() {
#Override
public String format(LogRecord arg0) {
StringBuilder b = new StringBuilder();
b.append(new Date());
b.append(" ");
b.append(arg0.getSourceClassName());
b.append(" ");
b.append(arg0.getSourceMethodName());
b.append(" ");
b.append(arg0.getLevel());
b.append(" ");
b.append(arg0.getMessage());
b.append(System.getProperty("line.separator"));
return b.toString();
}
};
fomatter in the java code
..... .....
java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.FileHandler.level=WARNING
**java.util.logging.??? = how can i configure my own formater in the property file with this information: data, clasename, methodename, level .etc.****
formatter in de log.proprties
Write your own Formatter that accepts a format String.
Configure your formatter in the logging.properties file. For example:
java.util.logging.ConsoleHandler.formatter = my.util.logging.CustomFormatter
my.util.logging.CustomFormatter.format = %t %L: %E %m
my.util.logging.CustomFormatter.date.format = ddMMMyyyy HH:mm:ss
# logger.log(Level.INFO, "INFO message.");
# produces the following log message:
# 18Aug2013 13:04:19 INFO: (LoggingLevelDemo.java:34) INFO message.
If writing such a Formatter seems daunting, Here is something to start with:
package my.util.logging;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.DateFormat;
import java.text.FieldPosition;
import java.text.Format;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.logging.Formatter;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
/**
* A {#link Formatter} that may be customized in a {#code logging.properties} file. The syntax of the property
* {#code my.util.logging.CustomFormatter.format} specifies the output. A newline will be appended to the string and
* the following special characters will be expanded (case sensitive):-
* <ul>
* <li>{#code %m} - message</li>
* <li>{#code %L} - log level</li>
* <li>{#code %n} - name of the logger</li>
* <li>{#code %t} - a timestamp (in ISO-8601 "yyyy-MM-dd HH:mm:ss.SSS" format)</li>
* <li>{#code %M} - source method name (if available, otherwise "?")</li>
* <li>{#code %c} - source class name (if available, otherwise "?")</li>
* <li>{#code %C} - source simple class name (if available, otherwise "?")</li>
* <li>{#code %T} - thread ID</li>
* <li>{#code %E} - (Filename.java:linenumber) Slow to generate Eclipse format</li>
* </ul>
* The default format is {#value #DEFAULT_FORMAT}. Curly brace characters are not allowed.
*
* Based on http://javablog.co.uk/2008/07/12/logging-with-javautillogging/
* %E Eclipse format was added with flag to avoid Stack trace generation if Eclipse format not used.
*
* #author Samuel Halliday
* #author Ryan Ripken
*/
public class CustomFormatter extends Formatter
{
// milliseconds can be nice for rough performance numbers
public static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS Z";
private static final DateFormat defaultDateFormat = new SimpleDateFormat(DATE_FORMAT);
// private static final String USACE_DATE_FORMAT = "yyyyMMdd HHmmss Z";
public static final String DEFAULT_FORMAT = "%t %L: %E %m";
protected static final StackTraceElement nullElement = new StackTraceElement("?", "?", "?", -1);
private final MessageFormat messageFormat;
private final boolean[] needsArg;
private final DateFormat dateFormat;
public CustomFormatter()
{
super();
LogManager logManager = LogManager.getLogManager();
String classname = getClass().getName();
// load the format from logging.properties
String dateFormatKey = classname + ".date.format";
String strDateFormat = logManager.getProperty(dateFormatKey);
if (strDateFormat != null) {
dateFormat = new SimpleDateFormat(strDateFormat);
} else {
dateFormat = defaultDateFormat;
}
String dateFormatTimeZoneKey = classname + ".date.timezone";
String strDateFormatTimeZone = logManager
.getProperty(dateFormatTimeZoneKey);
if (strDateFormatTimeZone != null) {
dateFormat.setTimeZone(TimeZone.getTimeZone(strDateFormatTimeZone));
}
String propName = classname + ".format";
String format = logManager.getProperty(propName);
if (format == null || format.trim().length() == 0) {
format = DEFAULT_FORMAT;
}
if (format.contains("{") || format.contains("}")) {
throw new IllegalArgumentException("curly braces not allowed");
}
// convert it into the MessageFormat form
format = format.replace("%L", "{0}")
.replace("%m", "{1}")
.replace("%M", "{2}")
.replace("%t", "{3}")
.replace("%c", "{4}")
.replace("%T", "{5}")
.replace("%n", "{6}")
.replace("%C", "{7}")
.replace("%E", "{8}") +
"\n";
messageFormat = new MessageFormat(format);
Format[] formatsByArgumentIndex = messageFormat.getFormatsByArgumentIndex();
needsArg = new boolean[9];
for (int i = 0; i < formatsByArgumentIndex.length; i++) {
needsArg[i] = format.contains("{" + i + "}");
}
}
#Override
public String format(LogRecord record)
{
String[] arguments = new String[9];
// This is a StringBuffer instead of StringBuilder so that
// it can be re-used for the messageFormat.format calls.
// It is, hopefully, slightly oversized so that it won't
// have to be enlarged...
StringBuffer sb = new StringBuffer(256);
if (needsArg[0]) {
// %L
arguments[0] = record.getLevel().toString();
}
if (needsArg[1]) {
// %m
sb = formatMessage(record, sb);
sb = getThrowableMessage(record, sb); // maybe this should have been its own flag?
arguments[1] = sb.toString();
}
if (needsArg[2]) {
// %M
arguments[2] = record.getSourceMethodName();
} else {
arguments[2] = "?";
}
if (needsArg[3]) {
// %t
sb.delete(0, sb.length()); // re-use
Date date = new Date(record.getMillis());
FieldPosition fieldPos = new FieldPosition(0);
synchronized (dateFormat) {
sb = dateFormat.format(date, sb, fieldPos);
}
arguments[3] = sb.toString();
}
if (needsArg[4] || needsArg[7]) {
// %c
arguments[4] = record.getSourceClassName();
} else {
arguments[4] = "?";
}
if (needsArg[5]) {
// %T
arguments[5] = Integer.toString(record.getThreadID());
}
if (needsArg[6]) {
// %n
arguments[6] = record.getLoggerName();
}
if (needsArg[7]) {
// %C
int start = arguments[4].lastIndexOf('.') + 1;
if (start > 0 && start < arguments[4].length()) {
arguments[7] = arguments[4].substring(start);
} else {
arguments[7] = arguments[4];
}
}
if (needsArg[8]) {
// %E Expensive Eclipse Format
sb.delete(0, sb.length()); // reuse buffer
getEclipseFormat(sb); // gets a stackTrace to generate.
arguments[8] = sb.toString();
} else {
arguments[8] = "(?:?)";
}
sb.delete(0, sb.length()); //reuse buffer
FieldPosition fieldPos = new FieldPosition(0);
synchronized (messageFormat) {
// messageFormat.format only calls into
// messageFormat.format(arguments, new StringBuffer(), new FieldPosition(0)).toString();
// we already have a StringBuffer laying around. We should reuse it.
//return messageFormat.format(arguments);
sb = messageFormat.format(arguments, sb, fieldPos);
}
return sb.toString();
}
/**
* Localize and format the message string from a log record.
* <p>
* The message string is first localized to a format string using the record's ResourceBundle. (If there is no
* ResourceBundle, or if the message key is not found, then the key is used as the format string.) The format String
* uses java.text style formatting.
* <ul>
* <li>If there are no parameters, no formatter is used.
* <li>Otherwise, if the string contains "{0" then java.text.MessageFormat is used to format the string.
* <li>Otherwise no formatting is performed.
* </ul>
* <p>
*
* #param record the log record containing the raw message
* #param result the StringBuffer where the message text is to be appended
* #return StringBuffer where the message text was appended
*/
public synchronized StringBuffer formatMessage(LogRecord record, StringBuffer result)
{
// This is the default formatMessage implementation from
// java.util.logging.Formatter except that it has been
// modified to operate on the passed in StringBuffer.
String format = record.getMessage();
java.util.ResourceBundle catalog = record.getResourceBundle();
if (catalog != null) {
try {
format = catalog.getString(record.getMessage());
} catch (java.util.MissingResourceException ex) {
// Drop through. Use record message as format
format = record.getMessage();
}
}
// Do the formatting.
try {
Object parameters[] = record.getParameters();
if (parameters == null || parameters.length == 0) {
// No parameters. Just return format string.
result.append(format);
} else if (format.indexOf("{0") >= 0 ||
format.indexOf("{1") >= 0 ||
format.indexOf("{2") >= 0 ||
format.indexOf("{3") >= 0) {
// Is is a java.text style format?
// Ideally we could match with
// Pattern.compile("\\{\\d").matcher(format).find())
// However the cost is 14% higher, so we cheaply check for
// 1 of the first 4 parameters
MessageFormat temp = new MessageFormat(format);
temp.format(parameters, result, new FieldPosition(0)); // this appends to sb
} else {
result.append(format);
}
} catch (Exception ex) {
// Formatting failed: use localized format string.
result.append(format);
}
return result;
}
protected StringBuffer getThrowableMessage(LogRecord record, StringBuffer sb)
{
Throwable thrown = record.getThrown();
if (thrown != null) {
PrintWriter pw = null;
try {
StringWriter sw = new StringWriter();
pw = new PrintWriter(sw);
record.getThrown().printStackTrace(pw);
pw.flush();
sb.append('\n');
sb.append(sw.toString());
} catch (Exception ex) {
System.out.println("CustomFormatter:Caught exception trying to build log message.");
ex.printStackTrace();
} finally {
if (pw != null) {
pw.close();
}
}
}
return sb;
}
/**
* Returns caller location information in eclipse format eg (Filename.java:23) WARNING Generating caller location
* information is extremely slow. It's use should be avoided unless execution speed is not an issue.
*
* #param sb
*
* #return the eclipse format
*/
public StringBuffer getEclipseFormat(StringBuffer sb)
{
final boolean useCurrentThread = false;
// getStackTrace can be expensive
StackTraceElement[] stackTrace;
if (useCurrentThread) {
stackTrace = Thread.currentThread().getStackTrace();
} else {
// Its possible this is faster.
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6375302
stackTrace = new Throwable().getStackTrace();
}
StackTraceElement element = findCallingElement(stackTrace);
appendEclipseFormat(element, sb);
return sb;
}
public static StringBuffer appendEclipseFormat(final StackTraceElement element, final StringBuffer sb)
{
if (element == null || nullElement == element) {
sb.append("(?:?)");
} else {
sb.append('(');
String filename = element.getFileName();
if (filename == null || filename.isEmpty()) {
sb.append("?");
} else {
sb.append(filename);
}
sb.append(':');
int lineNumber = element.getLineNumber();
if (lineNumber <= 0) {
sb.append('?');
} else {
sb.append(lineNumber);
}
sb.append(')');
}
return sb;
}
public static StackTraceElement findCallingElement(StackTraceElement[] stackTrace)
{
StackTraceElement retval = nullElement;
int lastIdx = firstIndexNot(stackTrace, Logger.class.getName(), 7);
if (lastIdx >= 0 && lastIdx < stackTrace.length) {
retval = stackTrace[lastIdx];
}
return retval;
}
private static int firstIndexNot(StackTraceElement[] stackTrace,
String classname, int fromIndex)
{
int idx = -1;
if (classname != null) {
if (stackTrace != null && fromIndex >= 0 && stackTrace.length > fromIndex) {
for (int i = fromIndex; i < stackTrace.length; i++) {
if (!classname.equals(stackTrace[i].getClassName())) {
idx = i;
break;
}
}
}
}
return idx;
}
}
Put the properties you want in the properties file.
In your Formatter call
LogManager theManager = LogManager.getLogManager();
String value = theManager.getProperty("propName");
// do whatever with value