package Testing;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
public class testing {
// map to store the number of errors per user
private static Map<String, Integer> errorsPerUser = new HashMap<>();
// variable to store the number of jobs started
private static int jobsStarted = 0;
// variable to store the number of jobs completed
private static int jobsCompleted = 0;
public static void main(String[] args) {
// specify the path to the log file
String filePath = "C:/Users/Wafiq/Documents/WIX1002/GroupAssignment/extracted_log.txt";
try (Scanner scanner = new Scanner(new File(filePath))) {
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
int timestampEndIndex = line.indexOf("]");
String lineWithoutTimestamp = line.substring(timestampEndIndex+2);
// check if line contains error message
if (lineWithoutTimestamp.contains("error: This association")) {
// extract the user from the line
String user = extractUser(lineWithoutTimestamp);
// increment the error count for the user
incrementErrorCount(user);
}
// check if line indicates job start
if (lineWithoutTimestamp.contains("sched: Allocate")) {
jobsStarted++;
}
// check if line indicates job completion
if (lineWithoutTimestamp.contains("_job_complete: JobId")) {
jobsCompleted++;
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// print the results
System.out.println("Number of jobs started: " + jobsStarted);
System.out.println("Number of jobs completed: " + jobsCompleted);
System.out.println("Number of errors per user:");
for (Map.Entry<String, Integer> entry : errorsPerUser.entrySet()) {
System.out.println(": " + entry.getValue());
}
}
// method to extract the user from the line
private static String extractUser(String line) {
// assuming the user is the string before "error" in the line
return line.substring(0, line.indexOf("error")).trim();
}
// method to increment the error count for the user
private static void incrementErrorCount(String user) {
if (errorsPerUser.containsKey(user)) {
errorsPerUser.put(user, errorsPerUser.get(user) + 1);
} else {
errorsPerUser.put(user, 1);
}
}
}
Output:
File data:
I'm trying to extract the number of jobs causing error and the corresponding user. I have done the number of jobs causing error but I don't know how to extract the number of corresponding user.
(p/s: Pls don't slander me, I'm a first year student in Comp Science. I have tried my best)
The user is not at the same index each line so I dont know how to extract it from the line.
While the user is not at the same index across lines, it always comes after user=' and ends on the next '. Search for these substrings in your line and you are done.
int startIndex = line.indexOf("user='");
if (startIndex>=0) {
int endIndex = line.indexOf("'", startIndex);
String user = line.substring(startIndex, endIndex);
System.out.println("user="+user);
} else {
System.out.println("no user in line");
}
Edit: I saw there is another pattern also in use. I think you can change the above algorithm to also allow for the second one.
Related
I am building a small Java utility (using Jackson) to catch errors in Java files, and one part of it is a text area, in which you might paste some JSON context and it will tell you the line and column where it's found it:
I am using the error message to take out the line and column as a string and print it out in the interface for someone using it.
This is the JSON sample I'm working with, and there is an intentional error beside "age", where it's missing a colon:
{
"name": "mkyong.com",
"messages": ["msg 1", "msg 2", "msg 3"],
"age" 100
}
What I want to do is also highlight the problematic area in a cyan color, and for that purpose, I have this code for the button that validates what's inserted in the text area:
cmdValidate.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
functionsClass ops = new functionsClass();
String JSONcontent = JSONtextArea.getText();
Results obj = new Results();
ops.validate_JSON_text(JSONcontent, obj);
String result = obj.getResult();
String caret = obj.getCaret();
//String lineNum = obj.getLineNum();
//showStatus(result);
if(result==null) {
textAreaError.setText("JSON code is valid!");
} else {
textAreaError.setText(result);
Highlighter.HighlightPainter cyanPainter;
cyanPainter = new DefaultHighlighter.DefaultHighlightPainter(Color.cyan);
int caretPosition = Integer.parseInt(caret);
int lineNumber = 0;
try {
lineNumber = JSONtextArea.getLineOfOffset(caretPosition);
} catch (BadLocationException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
try {
JSONtextArea.getHighlighter().addHighlight(lineNumber, caretPosition + 1, cyanPainter);
} catch (BadLocationException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
});
}
The "addHighlight" method works with a start range, end range and a color, which didn't become apparent to me immediately, thinking I had to get the reference line based on the column number. Some split functions to extract the numbers, I assigned 11 (in screenshot) to a caret value, not realizing that it only counts character positions from the beginning of the string and represents the end point of the range.
For reference, this is the class that does the work behind the scenes, and the error handling at the bottom is about extracting the line and column numbers. For the record, "x" is the error message that would generate out of an invalid file.
package parsingJSON;
import java.io.IOException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public class functionsClass extends JSONTextCompare {
public boolean validate_JSON_text(String JSONcontent, Results obj) {
boolean valid = false;
try {
ObjectMapper objMapper = new ObjectMapper();
JsonNode validation = objMapper.readTree(JSONcontent);
valid = true;
}
catch (JsonParseException jpe){
String x = jpe.getMessage();
printTextArea(x, obj);
//return part_3;
}
catch (IOException ioe) {
String x = ioe.getMessage();
printTextArea(x, obj);
//return part_3;
}
return valid;
}
public void printTextArea(String x, Results obj) {
// TODO Auto-generated method stub
System.out.println(x);
String err = x.substring(x.lastIndexOf("\n"));
String parts[] = err.split(";");
//String part 1 is the discarded leading edge that is the closing brackets of the JSON content
String part_2 = parts[1];
//split again to get rid of the closing square bracket
String parts2[] = part_2.split("]");
String part_3 = parts2[0];
//JSONTextCompare feedback = new JSONTextCompare();
//split the output to get the exact location of the error to communicate back and highlight it in the JSONTextCompare class
//first need to get the line number from the output
String[] parts_lineNum = part_3.split("line: ");
String[] parts_lineNum_final = parts_lineNum[1].split(", column:");
String lineNum = parts_lineNum_final[0];
String[] parts_caret = part_3.split("column: ");
String caret = parts_caret[1];
System.out.println(caret);
obj.setLineNum(lineNum);
obj.setCaret(caret);
obj.setResult(part_3);
System.out.println(part_3);
}
}
Screenshot for what the interface currently looks like:
Long story short - how do I turn the coordinates Line 4, Col 11 into a caret value (e.g. it's value 189, for the sake of argument) that I can use to get the highlighter to work properly. Some kind of custom parsing formula might be possible, but in general, is that even possible to do?
how do I turn the coordinates Line 4, Col 11 into a caret value (e.g. it's value 189,
Check out: Text Utilities for methods that might be helpful when working with text components. It has methods like:
centerLineInScrollPane
getColumnAtCaret
getLineAtCaret
getLines
gotoStartOfLine
gotoFirstWordOnLine
getWrappedLines
In particular the gotoStartOfLine() method contains code you can modify to get the offset of the specified row/column.offset.
The basic code would be:
int line = 4;
int column = 11;
Element root = textArea.getDocument().getDefaultRootElement();
int offset = root.getElement( line - 1 ).getStartOffset() + column;
System.out.println(offset);
The way it works is essentially counting the number of characters in each line, up until the line in which the error is occurring, and adding the caretPosition to that sum of characters, which is what the Highlighter needs to apply the marking to the correct location.
I've added the code for the Validate button for context.
functionsClass ops = new functionsClass();
String JSONcontent = JSONtextArea.getText();
Results obj = new Results();
ops.validate_JSON_text(JSONcontent, obj);
String result = obj.getResult();
String caret = obj.getCaret();
String lineNum = obj.getLineNum();
//showStatus(result);
if(result==null) {
textAreaError.setText("JSON code is valid!");
} else {
textAreaError.setText(result);
Highlighter.HighlightPainter cyanPainter;
cyanPainter = new DefaultHighlighter.DefaultHighlightPainter(Color.cyan);
//the column number as per the location of the error
int caretPosition = Integer.parseInt(caret); //JSONtextArea.getCaretPosition();
//the line number as per the location of the error
int lineNumber = Integer.parseInt(lineNum);
//get the number of characters in the string up to the line in which the error is found
int totalChars = 0;
int counter = 0; //used to only go to the line above where the error is located
String[] lines = JSONcontent.split("\\r?\\n");
for (String line : lines) {
counter = counter + 1;
//as long as we're above the line of the error (lineNumber variable), keep counting characters
if (counter < lineNumber)
{
totalChars = totalChars + line.length();
}
//if we are at the line that contains the error, only add the caretPosition value to get the final position where the highlighting should go
if (counter == lineNumber)
{
totalChars = totalChars + caretPosition;
break;
}
}
//put down the highlighting in the area where the JSON file is having a problem
try {
JSONtextArea.getHighlighter().addHighlight(totalChars - 2, totalChars + 2, cyanPainter);
} catch (BadLocationException e1) {
// TODO Auto-generated catch block
e1.getMessage();
}
}
The contents of the JSON file is treated as a string, and that's why I'm also iterating through it in that fashion. There are certainly better ways to go through lines in the string, and I'll add some reference topics on SO:
What is the easiest/best/most correct way to iterate through the characters of a string in Java? - Link
Check if a string contains \n - Link
Split Java String by New Line - Link
What is the best way to iterate over the lines of a Java String? - Link
Generally a combination of these led to this solution, and I am also not targeting it for use on very large JSON files.
A screenshot of the output, with the interface highlighting the same area that Notepad++ would complain about, if it could debug code:
I'll post the project on GitHub after I clean it up and comment it some, and will give a link to that later, but for now, hopefully this helps the next dev in a similar situation.
I am learning how to work with files in Java. I have a sample file which contains key pairs and it values. I am trying to find a key pairs and if it matches, then output file would be updated with both, key pair and it's value. I am able to get key pairs in output file but unable to get values too. Stringbuilder may work here to append strings but I don't know how.
Below are my input and output files.
Input File:
born time 9 AM London -- kingNumber 1234567890 -- address: abc/cd/ef -- birthmonth: unknown
born time 9 AM Europe -- kingNumber 1234567890 -- address: abc/cd/ef -- birthmonth: december
Expected Output File:
kingNumber 1234567890 birthmonth unknown
kingNumber 1234567890 birthmonth unkbown
Current Output File:
kingNumber birthmonth
kingNumber birthmonth
I am able to write key pair ("kingNumber" and "birthmonth" in this case) to output file but I am not sure what I can do to get it's value too.
String kn = "kingNumber:";
String bd = "birthmonth:";
try {
File f = new File("sample.txt");
Scanner sc = new Scanner(f);
FileWriter fw = new FileWriter("output.txt");
while(sc.hasNextLine()) {
String lineContains = sc.next();
if(lineContains.contains(kn)) {
fw.write(kn + "\n");
// This is where I am stuck. What
// can I do to get it's value (number in this case).
}
else if(lineContains.contains(bd)) {
fw.write(bd);
// This is where I am stuck. What
// can I do to get it's value (birthday in this case).
}
}
} catch (IOException e) {
e.printStackTrace();
}
you could use java.util.regex.Pattern & java.util.regex.Matcherwith a pattern alike:
^born\stime\s([a-zA-Z0-9\s]*)\s--\skingNumber\s(\d+)\s--\saddress:\s([a-zA-Z0-9\s/]*)\s--\sbirthmonth:\s([a-zA-Z0-9\s]*)$
write less, do more.
I have written a simple parser that it following data format from your example.
You will need to call it like this:
PairParser parser = new PairParser(lineContains);
then you can get value from the parser by pair keys
How to get value:
parser.getValue("kingNumber")
Note that keys do not have trailing column character.
The parser code is here:
package com.grenader.example;
import java.util.HashMap;
import java.util.Map;
public class PairParser {
private Map<String, String> data = new HashMap<>();
/**
* Constructor, prepare the data
* #param dataString line from the given data file
*/
public PairParser(String dataString) {
if (dataString == null || dataString.isEmpty())
throw new IllegalArgumentException("Data line cannot be empty");
// Spit the input line into array of string blocks based on '--' as a separator
String[] blocks = dataString.split("--");
for (String block : blocks)
{
if (block.startsWith("born time")) // skip this one because it doesn't looks like a key/value pair
continue;
String[] strings = block.split("\\s");
if (strings.length != 3) // has not exactly 3 items (first items is empty), skipping this one as well
continue;
String key = strings[1];
String value = strings[2];
if (key.endsWith(":"))
key = key.substring(0, key.length()-1).trim();
data.put(key.trim(), value.trim());
}
}
/**
* Return value based on key
* #param key
* #return
*/
public String getValue(String key)
{
return data.get(key);
}
/**
* Return number of key/value pairs
* #return
*/
public int size()
{
return data.size();
}
}
And here is the Unit Test to make sure that the code works
package com.grenader.example;
import com.grenader.example.PairParser;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public class PairParserTest {
#Test
public void getValue_Ok() {
PairParser parser = new PairParser("born time 9 AM London -- kingNumber 1234567890 -- address: abc/cd/ef -- birthmonth: unknown");
assertEquals("1234567890", parser.getValue("kingNumber"));
assertEquals("unknown", parser.getValue("birthmonth"));
}
#Test(expected = IllegalArgumentException.class)
public void getValue_Null() {
new PairParser(null);
fail("This test should fail with Exception");
}
#Test(expected = IllegalArgumentException.class)
public void getValue_EmptyLine() {
new PairParser("");
fail("This test should fail with Exception");
}
#Test()
public void getValue_BadData() {
PairParser parser = new PairParser("bad data bad data");
assertEquals(0, parser.size());
}
}
I am trying to read a file of string int and boolean values into an array list as object blocks. The string values go into the array list just fine, its the boolean values I'm having trouble with. Every time I encounter the variable 'active'there is a mismatch exception. Please help! The text file for if the block is a wizard goes in this order
name (string)
location (string)
active (boolean) ... the one I'm having issues with
skill level (int)
friendliness (int)
I included the driver class as well as the Witch class which contains the
variable 'active' originally.
Driver class that adds objects to the array list based on what the scanner
reads from the file
package project2;
import java.util.Scanner;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
public class Project2 {
public static void main(String[] args) {
Scanner inputFileScanner1 = null;
//file name
String listFile = "list.txt";
// Check to see if file exists
try {
inputFileScanner1 = new Scanner(new File(listFile));
} catch (FileNotFoundException e) {
System.out.println("Error opening file.");
System.exit(1);
}
//create Individuals arraylist and Location arraylist
ArrayList < Individual > Individual = new ArrayList < > ();
ArrayList < String > Location = new ArrayList < > ();
//declare variables to read file contents into the arraylist
String wizName, witchName, individualName, location, position,
profession = null, line = null;
int wizLevel, witchSkillLevel, friendliness;
boolean active;
//while there is a next line, if the line equals Wizard, the next four lines
// are wizard name, location, position and level
while (inputFileScanner1.hasNext()) {
line = inputFileScanner1.nextLine();
if (line.trim().equals("Wizard")) {
wizName = inputFileScanner1.nextLine().trim();
location = inputFileScanner1.nextLine().trim();
position = inputFileScanner1.nextLine().trim();
wizLevel = inputFileScanner1.nextInt();
//create wizard object
Individual wizard = new Wizard(wizName, location, position, profession, wizLevel);
//fill arraylist with wizard objects
Individual.add(wizard);
Location.add(location);
} //if the next line is Witch, the next five lines are
// witch name, location, yes/no active, skill level, and friendliness
//in that order
else if (line.trim().equals("Witch")) {
witchName = inputFileScanner1.nextLine().trim();
location = inputFileScanner1.nextLine().trim();
active = inputFileScanner1.nextBoolean();
witchSkillLevel = inputFileScanner1.nextInt();
friendliness = inputFileScanner1.nextInt();
//create witch object
Individual witch = new Witch(witchName, location, profession, witchSkillLevel, friendliness, active);
//fill the arraylist with witch objects
Individual.add(witch);
Location.add(location);
} else {
profession = line.trim();
individualName = inputFileScanner1.nextLine().trim();
location = inputFileScanner1.nextLine().trim();
Individual i = new Individual(profession, individualName, location);
Individual.add(i);
Location.add(location);
}
java.util.Collections.sort(Individual);
java.util.Collections.sort(Location);
}
System.out.println("List of friends and possible allies: " + Location);
inputFileScanner1.close();
}
}
//Witch class which holds values that are in the text file. active is the boolean value Im having trouble with
package project2;
public class Witch extends Individual implements Magical {
private int skill;
private int friendly;
//Constructor with witch parameters
public Witch(String name, String location, String profession,
int skill, int friendly, boolean active) {
}
//default constructor
public Witch() {
this("", "", "", 0, 0, false);
}
//overridden abstract method from magical interface
#Override
public void assess() {
System.out.print(this.friendly + " " + this.skill + " " + super.toString());
}
}
<!-- end snippet -->
Text file :
enter image description here
When you pull in your boolean variable do something like this.
if(inputFileScanner1.nextLine().trim().equals("yes"))
{
active = true;
}
else
{
active = false;
}
Okay, the problem is that the file contains the strings yes and no, that are not directly parsable as booleans (should be true or false).
If you can change the original data file somehow, I would suggest to use the two true and false keywords, otherwise, the #Sendrick Jefferson solution will do the job (at your own risk: every typo, as for instance "ye", will be translated into false).
I am looking for an idea how to accomplish this task. So I'll start with how my program is working.
My program reads a CSV file. They are key value pairs separated by a comma.
L1234456,ygja-3bcb-iiiv-pppp-a8yr-c3d2-ct7v-giap-24yj-3gie
L6789101,zgna-3mcb-iiiv-pppp-a8yr-c3d2-ct7v-gggg-zz33-33ie
etc
Function takes a file and parses it into an arrayList of String[]. The function returns the ArrayList.
public ArrayList<String[]> parseFile(File csvFile) {
Scanner scan = null;
try {
scan = new Scanner(csvFile);
} catch (FileNotFoundException e) {
}
ArrayList<String[]> records = new ArrayList<String[]>();
String[] record = new String[2];
while (scan.hasNext()) {
record = scan.nextLine().trim().split(",");
records.add(record);
}
return records;
}
Here is the code, where I am calling parse file and passing in the CSVFile.
ArrayList<String[]> Records = parseFile(csvFile);
I then created another ArrayList for files that aren't parsed.
ArrayList<String> NotParsed = new ArrayList<String>();
So the program then continues to sanitize the key value pairs separated by a comma. So we first start with the first key in the record. E.g L1234456. If the record could not be sanitized it then it replaces the current key with "CouldNOtBeParsed" text.
for (int i = 0; i < Records.size(); i++) {
if(!validateRecord(Records.get(i)[0].toString())) {
Logging.info("Records could not be parsed " + Records.get(i)[0]);
NotParsed.add(srpRecords.get(i)[0].toString());
Records.get(i)[0] = "CouldNotBeParsed";
} else {
Logging.info(Records.get(i)[0] + " has been sanitized");
}
}
Next we do the 2nd key in the key value pair e.g ygja-3bcb-iiiv-pppp-a8yr-c3d2-ct7v-giap-24yj-3gie
for (int i = 0; i < Records.size(); i++) {
if(!validateRecordKey(Records.get(i)[1].toString())) {
Logging.info("Record Key could not be parsed " + Records.get(i)[0]);
NotParsed.add(Records.get(i)[1].toString());
Records.get(i)[1] = "CouldNotBeParsed";
} else {
Logging.info(Records.get(i)[1] + " has been sanitized");
}
}
The problem is that I need both keyvalue pairs to be sanitized, make a separate list of the keyValue pairs that could not be sanitized and a list of the ones there were sanitized so they can be inserted into a database. The ones that cannot will be printed out to the user.
I thought about looping thought the records and removing the records with the "CouldNotBeParsed" text so that would just leave the ones that could be parsed. I also tried removing the records from the during the for loop Records.remove((i)); However that messes up the For loop because if the first record could not be sanitized, then it's removed, the on the next iteration of the loop it's skipped because record 2 is now record 1. That's why i went with adding the text.
Atually I need two lists, one for the Records that were sanitized and another that wasn't.
So I was thinking there must be a better way to do this. Or a better method of sanitizing both keyValue pairs at the same time or something of that nature. Suggestions?
Start by changing the data structure: rather than using a list of two-element String[] arrays, define a class for your key-value pairs:
class KeyValuePair {
private final String key;
private final String value;
public KeyValuePair(String k, String v) { key = k; value = v; }
public String getKey() { return key; }
public String getValue() { return value; }
}
Note that the class is immutable.
Now make an object with three lists of KeyValuePair objects:
class ParseResult {
private final List<KeyValuePair> sanitized = new ArrayList<KeyValuePair>();
private final List<KeyValuePair> badKey = new ArrayList<KeyValuePair>();
private final List<KeyValuePair> badValue = new ArrayList<KeyValuePair>();
public ParseResult(List<KeyValuePair> s, List<KeyValuePair> bk, List<KeyValuePair> bv) {
sanitized = s;
badKey = bk;
badValue = bv;
}
public List<KeyValuePair> getSanitized() { return sanitized; }
public List<KeyValuePair> getBadKey() { return badKey; }
public List<KeyValuePair> getBadValue() { return badValue; }
}
Finally, populate these three lists in a single loop that reads from the file:
public static ParseResult parseFile(File csvFile) {
Scanner scan = null;
try {
scan = new Scanner(csvFile);
} catch (FileNotFoundException e) {
???
// Do something about this exception.
// Consider not catching it here, letting the caller deal with it.
}
final List<KeyValuePair> sanitized = new ArrayList<KeyValuePair>();
final List<KeyValuePair> badKey = new ArrayList<KeyValuePair>();
final List<KeyValuePair> badValue = new ArrayList<KeyValuePair>();
while (scan.hasNext()) {
String[] tokens = scan.nextLine().trim().split(",");
if (tokens.length != 2) {
???
// Do something about this - either throw an exception,
// or log a message and continue.
}
KeyValuePair kvp = new KeyValuePair(tokens[0], tokens[1]);
// Do the validation on the spot
if (!validateRecordKey(kvp.getKey())) {
badKey.add(kvp);
} else if (!validateRecord(kvp.getValue())) {
badValue.add(kvp);
} else {
sanitized.add(kvp);
}
}
return new ParseResult(sanitized, badKey, badValue);
}
Now you have a single function that produces a single result with all your records cleanly separated into three buckets - i.e. sanitized records, records with bad keys, and record with good keys but bad values.
DurationOfRun:5
ThreadSize:10
ExistingRange:1-1000
NewRange:5000-10000
Percentage:55 - AutoRefreshStoreCategories Data:Previous/30,New/70 UserLogged:true/50,false/50 SleepTime:5000 AttributeGet:1,16,10106,10111 AttributeSet:2060/30,10053/27
Percentage:25 - CrossPromoEditItemRule Data:Previous/60,New/40 UserLogged:true/50,false/50 SleepTime:4000 AttributeGet:1,10107 AttributeSet:10108/34,10109/25
Percentage:20 - CrossPromoManageRules Data:Previous/30,New/70 UserLogged:true/50,false/50 SleepTime:2000 AttributeGet:1,10107 AttributeSet:10108/26,10109/21
I am trying to parse above .txt file(first four lines are fixed and last three Lines can increase means it can be more than 3), so for that I wrote the below code and its working but it looks so messy. so Is there any better way to parse the above .txt file and also if we consider performance then which will be best way to parse the above txt file.
private static int noOfThreads;
private static List<Command> commands;
public static int startRange;
public static int endRange;
public static int newStartRange;
public static int newEndRange;
private static BufferedReader br = null;
private static String sCurrentLine = null;
private static List<String> values;
private static String commandName;
private static String percentage;
private static List<String> attributeIDGet;
private static List<String> attributeIDSet;
private static LinkedHashMap<String, Double> dataCriteria;
private static LinkedHashMap<Boolean, Double> userLoggingCriteria;
private static long sleepTimeOfCommand;
private static long durationOfRun;
br = new BufferedReader(new FileReader("S:\\Testing\\PDSTest1.txt"));
values = new ArrayList<String>();
while ((sCurrentLine = br.readLine()) != null) {
if(sCurrentLine.startsWith("DurationOfRun")) {
durationOfRun = Long.parseLong(sCurrentLine.split(":")[1]);
} else if(sCurrentLine.startsWith("ThreadSize")) {
noOfThreads = Integer.parseInt(sCurrentLine.split(":")[1]);
} else if(sCurrentLine.startsWith("ExistingRange")) {
startRange = Integer.parseInt(sCurrentLine.split(":")[1].split("-")[0]);
endRange = Integer.parseInt(sCurrentLine.split(":")[1].split("-")[1]);
} else if(sCurrentLine.startsWith("NewRange")) {
newStartRange = Integer.parseInt(sCurrentLine.split(":")[1].split("-")[0]);
newEndRange = Integer.parseInt(sCurrentLine.split(":")[1].split("-")[1]);
} else {
attributeIDGet = new ArrayList<String>();
attributeIDSet = new ArrayList<String>();
dataCriteria = new LinkedHashMap<String, Double>();
userLoggingCriteria = new LinkedHashMap<Boolean, Double>();
percentage = sCurrentLine.split("-")[0].split(":")[1].trim();
values = Arrays.asList(sCurrentLine.split("-")[1].trim().split("\\s+"));
for(String s : values) {
if(s.startsWith("Data")) {
String[] data = s.split(":")[1].split(",");
for (String n : data) {
dataCriteria.put(n.split("/")[0], Double.parseDouble(n.split("/")[1]));
}
//dataCriteria.put(data.split("/")[0], value)
} else if(s.startsWith("UserLogged")) {
String[] userLogged = s.split(":")[1].split(",");
for (String t : userLogged) {
userLoggingCriteria.put(Boolean.parseBoolean(t.split("/")[0]), Double.parseDouble(t.split("/")[1]));
}
//userLogged = Boolean.parseBoolean(s.split(":")[1]);
} else if(s.startsWith("SleepTime")) {
sleepTimeOfCommand = Long.parseLong(s.split(":")[1]);
} else if(s.startsWith("AttributeGet")) {
String[] strGet = s.split(":")[1].split(",");
for(String q : strGet) attributeIDGet.add(q);
} else if(s.startsWith("AttributeSet:")) {
String[] strSet = s.split(":")[1].split(",");
for(String p : strSet) attributeIDSet.add(p);
} else {
commandName = s;
}
}
Command command = new Command();
command.setName(commandName);
command.setExecutionPercentage(Double.parseDouble(percentage));
command.setAttributeIDGet(attributeIDGet);
command.setAttributeIDSet(attributeIDSet);
command.setDataUsageCriteria(dataCriteria);
command.setUserLoggingCriteria(userLoggingCriteria);
command.setSleepTime(sleepTimeOfCommand);
commands.add(command);
Well, parsers usually are messy once you get down to the lower layers of them :-)
However, one possible improvement, at least in terms of code quality, would be to recognize the fact that your grammar is layered.
By that, I mean every line is an identifying token followed by some properties.
In the case of DurationOfRun, ThreadSize, ExistingRange and NewRange, the properties are relatively simple. Percentage is somewhat more complex but still okay.
I would structure the code as (pseudo-code):
def parseFile (fileHandle):
while (currentLine = fileHandle.getNextLine()) != EOF:
if currentLine.beginsWith ("DurationOfRun:"):
processDurationOfRun (currentLine[14:])
elsif currentLine.beginsWith ("ThreadSize:"):
processThreadSize (currentLine[11:])
elsif currentLine.beginsWith ("ExistingRange:"):
processExistingRange (currentLine[14:])
elsif currentLine.beginsWith ("NewRange:"):
processNewRange (currentLine[9:])
elsif currentLine.beginsWith ("Percentage:"):
processPercentage (currentLine[11:])
else
raise error
Then, in each of those processWhatever() functions, you parse the remainder of the line based on the expected format. That keeps your code small and readable and easily changed in future, without having to navigate a morass :-)
For example, processDurationOfRun() simply gets an integer from the remainder of the line:
def processDurationOfRun (line):
this.durationOfRun = line.parseAsInt()
Similarly, the functions for the two ranges split the string on - and get two integers from the resultant values:
def processExistingRange (line):
values[] = line.split("-")
this.existingRangeStart = values[0].parseAsInt()
this.existingRangeEnd = values[1].parseAsInt()
The processPercentage() function is the tricky one but that is also easily doable if you layer it as well. Assuming those things are always in the same order, it consists of:
an integer;
a literal -;
some sort of textual category; and
a series of key:value pairs.
And even these values within the pairs can be parsed by lower levels, splitting first on commas to get subvalues like Previous/30 and New/70, then splitting each of those subvalues on slashes to get individual items. That way, a logical hierarchy can be reflected in your code.
Unless you're expecting to be parsing this text files many times per second, or unless it's many megabytes in size, I'd be more concerned about the readability and maintainability of your code than the speed of the parsing.
Mostly gone are the days when we need to wring the last ounce of performance from our code but we still have problems in fixing said code in a timely manner when bugs are found or enhancements are desired.
Sometimes it's preferable to optimise for readability.
I would not worry about performance until I was sure there was actually a performance issue. Regarding the rest of the code, if you won't be adding any new line types I would not worry about it. If you do worry about it, however, a factory design pattern can help you separate the selection of the type of processing needed from the actual processing. It makes adding new line types easier without introducing as much opportunity for error.
The younger and more convenient class is Scanner. You just need to modify the delimiter, and get reading of data in the desired format (readInt, readLong) in one go - no need for separate x.parseX - calls.
Second: Split your code into small, reusable pieces. They make the program readable, and you can hide details easily.
Don't hesitate to use a struct-like class for a range, for example. Returning multiple values from a method can be done by these, without boilerplate (getter,setter,ctor).
import java.util.*;
import java.io.*;
public class ReadSampleFile
{
// struct like classes:
class PercentageRow {
public int percentage;
public String name;
public int dataPrevious;
public int dataNew;
public int userLoggedTrue;
public int userLoggedFalse;
public List<Integer> attributeGet;
public List<Integer> attributeSet;
}
class Range {
public int from;
public int to;
}
private int readInt (String name, Scanner sc) {
String s = sc.next ();
if (s.startsWith (name)) {
return sc.nextLong ();
}
else err (name + " expected, found: " + s);
}
private long readLong (String name, Scanner sc) {
String s = sc.next ();
if (s.startsWith (name)) {
return sc.nextInt ();
}
else err (name + " expected, found: " + s);
}
private Range readRange (String name, Scanner sc) {
String s = sc.next ();
if (s.startsWith (name)) {
Range r = new Range ();
r.from = sc.nextInt ();
r.to = sc.nextInt ();
return r;
}
else err (name + " expected, found: " + s);
}
private PercentageLine readPercentageLine (Scanner sc) {
// reuse above methods
PercentageLine percentageLine = new PercentageLine ();
percentageLine.percentage = readInt ("Percentage", sc);
// ...
return percentageLine;
}
public ReadSampleFile () throws FileNotFoundException
{
/* I only read from my sourcefile for convenience.
So I could scroll up to see what's the next entry.
Don't do this at home. :) The dummy later ...
*/
Scanner sc = new Scanner (new File ("./ReadSampleFile.java"));
sc.useDelimiter ("[ \n/,:-]");
// ... is the comment I had to insert.
String dummy = sc.nextLine ();
List <String> values = new ArrayList<String> ();
if (sc.hasNext ()) {
// see how nice the data structure is reflected
// by this code:
long duration = readLong ("DurationOfRun");
int noOfThreads = readInt ("ThreadSize");
Range eRange = readRange ("ExistingRange");
Range nRange = readRange ("NewRange");
List <PercentageRow> percentageRows = new ArrayList <PercentageRow> ();
// including the repetition ...
while (sc.hasNext ()) {
percentageRows.add (readPercentageLine ());
}
}
}
public static void main (String args[]) throws FileNotFoundException
{
new ReadSampleFile ();
}
public static void err (String msg)
{
System.out.println ("Err:\t" + msg);
}
}