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.
This is my whole code, it's quite complex but please help me. It's taken me for 2 days but I failed:
public static ArrayList<DocGia> XuatDocGia() throws IOException {
ArrayList<DocGia> listDocGia = new ArrayList<>();
File fileDocGia = new File("fileDocGia.txt");
if(fileDocGia.exists() == false) {
System.out.println("Chưa có đọc giả nào trong thư viện");
} else {
BufferedReader br = new BufferedReader(new FileReader("fileDocGia.txt"));
if (br.readLine() == null) {
System.out.println("Chưa có đọc giả nào trong thư viện");
} else {
int soDong = DemSoDong("fileDocGia.txt");
int dongHienTai = 0;
Scanner fileScanner = new Scanner(fileDocGia);
for(int i = 0, z = 0;;i++, z++) {
DocGia docGia = null;
System.out.println("***Đọc giả thứ: " + (i+1));
docGia.tendocgia = fileScanner.nextLine();
if(i >= 1) {
docGia.tendocgia = fileScanner.nextLine();
}
docGia.maDocGia = fileScanner.nextLine();
docGia.soSachmuon = fileScanner.nextInt();
docGia.thoiGianMuonSach = fileScanner.nextInt();
listDocGia.add(docGia);
docGia.XuatDocGia();
dongHienTai += 4;
if(dongHienTai == soDong) {
fileScanner.close();
break;
}
}
}
for(DocGia docGia: listDocGia) {
docGia.XuatDocGia();
}
}
return listDocGia;
}
look at my code, when i run:
docGia.XuatDocGia();
-> the value of every single element is right at debug. it also means the value of the variable inside is right. but at the end of this function. i run
for(DocGia docGia: listDocGia) {
docGia.XuatDocGia();
}
this is XuatDocGia funtion:
public static void XuatDocGia(){
System.out.println(tendocgia);
System.out.println(maDocGia);
System.out.println(soSachmuon);
System.out.println(thoiGianMuonSach);
}
It just shows for me the last element in this ArrayList, repeat in 3 times( equal the number of elements).
I think a problem come from adding process of listDocGia.add(docGia);
You guys no need to bother everything else in my code, because i know it's really complex. I have tested carefully, just focus on my problem.
I'm so sorry because i'm Vietnamese and beginner at Java. The next time everything will be English. Thank you so much.
If this is the actual code, you are adding null references to your List, but since you are using a static method to print the values, you don't get a NullPointerException. Assuming your code passes compilation, this means all the members of the DocGia class are static, which explains why you get the same values in each iteration of your loop.
You should change
DocGia docGia = null;
to
DocGia docGia = new DocGia ();
and change all the members of DocGia (including the XuatDocGia method that prints them) to be non static.
I have a simple textfile:
type = "hello"
number = 66
type = "hey"
number = 77
I'm basically just checking to see if the string "type" is found in the textfile, if yes, I'd also like to get the values "hello" and "hey" i.e the types. However, my program doesn't work at all in that sense it all always prints no, when checking if "type" is in the file.
public static void main(String[] args) {
if(args.length == 1)
parseText(argv[0]); // call with textfile name
}
public void parseText(String inPath) {
try {
Scanner s = new Scanner(inPath);
while(s.hasNextLine()) {
if("type".equals(s.nextLine().trim()))
System.out.println("Yes");
else {
System.out.println("no");
}
}
}catch (Exception e ) {
System.out.println("\nFILE NOT FOUND");
}
}
Any help would be much appreciated.
if("type".equals(s.nextLine().trim()))
You really want to read the line of data from the file into a String so you can do processing on the string.
You want to know if the line starts with "type":
String line = s.nextLine()
if (line.startsWith("type"))
{
String value = line.substring(8);
System.out.println(value);
}
Check if the line starts with "type" (not is equal to "type").
if("type".equals(s.nextLine().trim()))
should be something like
String line = s.nextLine().trim();
if (line.startsWith("type"))
I am just starting with Lucene so it's probably a beginners question. We are trying to implement a semantic search on digital books and already have a concept generator, so for example the contexts I generate for a new article could be:
|Green Beans | Spring Onions | Cooking |
I am using Lucene to create an index on the books/articles using only the extracted concepts (stored in a temporary document for that purpose). Now the standard analyzer is creating single word tokens: Green, Beans, Spring, Onions, Cooking, which of course is not the same.
My question: is there an analyzer that is able to detect delimiters around tokens (|| in our example), or an analyzer that is able to detect multi-word constructs?
I'm afraid we'll have to create our own analyzer, but I don't quite know where to start for that one.
Creating an analyzer is pretty easy. An analyzer is just a tokenizer optionally followed by token filters. In your case, you'd have to create your own tokenizer. Fortunately, you have a convenient base class for this: CharTokenizer.
You implement the isTokenChar method and make sure it returns false on the | character and true on any other character. Everything else will be considered part of a token.
Once you have the tokenizer, the analyzer should be straightforward, just look at the source code of any existing analyzer and do likewise.
Oh, and if you can have spaces between your | chars, just add a TrimFilter to the analyzer.
I came across this question because I am doing something with my Lucene mechanisms which creates data structures to do with sequencing, in effect "hijacking" the Lucene classes. Otherwise I can't imagine why people would want knowledge of the separators ("delimiters") between tokens, but as it was quite tricky I thought I'd put it here for the benefit of anyone who might need to.
You have to rewrite your own versions of StandardTokenizer and StandardTokenizerImpl. These are both final classes so you can't extend them.
SeparatorDeliveringTokeniserImpl (tweaked from source of StandardTokenizerImpl):
3 new fields:
private int startSepPos = 0;
private int endSepPos = 0;
private String originalBufferAsString;
Tweak these methods:
public final void getText(CharTermAttribute t) {
t.copyBuffer(zzBuffer, zzStartRead, zzMarkedPos - zzStartRead);
if( originalBufferAsString == null ){
originalBufferAsString = new String( zzBuffer, 0, zzBuffer.length );
}
// startSepPos == -1 is a "flag condition": it means that this token is the last one and it won't be followed by a sep
if( startSepPos != -1 ){
// if the flag is NOT set, record the start pos of the next sep...
startSepPos = zzMarkedPos;
}
}
public final void yyreset(java.io.Reader reader) {
zzReader = reader;
zzAtBOL = true;
zzAtEOF = false;
zzEOFDone = false;
zzEndRead = zzStartRead = 0;
zzCurrentPos = zzMarkedPos = 0;
zzFinalHighSurrogate = 0;
yyline = yychar = yycolumn = 0;
zzLexicalState = YYINITIAL;
if (zzBuffer.length > ZZ_BUFFERSIZE)
zzBuffer = new char[ZZ_BUFFERSIZE];
// reset fields responsible for delivering separator...
originalBufferAsString = null;
startSepPos = 0;
endSepPos = 0;
}
(inside getNextToken:)
if ((zzAttributes & 1) == 1) {
zzAction = zzState;
zzMarkedPosL = zzCurrentPosL;
if ((zzAttributes & 8) == 8) {
// every occurrence of a separator char leads here...
endSepPos = zzCurrentPosL;
break zzForAction;
}
}
And make a new method:
String getPrecedingSeparator() {
String sep = null;
if( originalBufferAsString == null ){
sep = new String( zzBuffer, 0, endSepPos );
}
else if( startSepPos == -1 || endSepPos <= startSepPos ){
sep = "";
}
else {
sep = originalBufferAsString.substring( startSepPos, endSepPos );
}
if( zzMarkedPos < startSepPos ){
// ... then this is a sign that the next token will be the last one... and will NOT have a trailing separator
// so set a "flag condition" for next time this method is called
startSepPos = -1;
}
return sep;
}
SeparatorDeliveringTokeniser (tweaked from source of StandardTokenizer):
Add this:
private String separator;
String getSeparator(){
// normally this delivers a preceding separator... but after incrementToken returns false, if there is a trailing
// separator, it then delivers that...
return separator;
}
(inside incrementToken:)
while(true) {
int tokenType = scanner.getNextToken();
// added NB this gives you the separator which PRECEDES the token
// which you are about to get from scanner.getText( ... )
separator = scanner.getPrecedingSeparator();
if (tokenType == SeparatorDeliveringTokeniserImpl.YYEOF) {
// NB at this point sep is equal to the trailing separator...
return false;
}
...
Usage:
In my FilteringTokenFilter subclass, called TokenAndSeparatorExamineFilter, the methods accept and end look like this:
#Override
public boolean accept() throws IOException {
String sep = ((SeparatorDeliveringTokeniser) input).getSeparator();
// a preceding separator can only be an empty String if we are currently
// dealing with the first token and if the sequence starts with a token
if (!sep.isEmpty()) {
// ... do something with the preceding separator
}
// then get the token...
String token = getTerm();
// ... do something with the token
// my filter does no filtering! Every token is accepted...:
return true;
}
#Override
public void end() throws IOException {
// deals with trailing separator at the end of a sequence of tokens and separators (if there is one, i.e. if it doesn't end with a token)
String sep = ((SeparatorDeliveringTokeniser) input).getSeparator();
// NB will be an empty String if there is no trailing separator
if (!sep.isEmpty()) {
// ... do something with this trailing separator
}
}
I'm using this method
static void readFile()
{
try
{
FileInputStream fstream = new FileInputStream( "Insurances.txt" );
BufferedReader br = new BufferedReader( new InputStreamReader( fstream ) );
String strLine;
String [] array;
while ( ( strLine = br.readLine() ) != null )
{
array = strLine.split( "," );
pword++;
int type = Integer.parseInt(array[0]);
int tm = Integer.parseInt(array[1]);
int year = Integer.parseInt(array[2]);
int month = Integer.parseInt(array[3]);
int day = Integer.parseInt(array[4]);
int pass = Integer.parseInt(array[5]);
double tc = 0;
if ( type==2 )
{
String man = array[6];
String mod = array[7];
int cc = Integer.parseInt(array[8]);
String lp = array[9];
String ex = array [10];
boolean extra;
if ( ex.equals("true") )
{
extra = true;
}
else
{
extra = false;
}
insurances[insur] = new CarInsurance( pword, type, tm, year, month, day, pass, tc, man, mod, cc, lp, extra );
tc = insurances[insur].calculateCost( pass, a, insur );
insurances[insur].setCost( tc );
insur++;
}
else
{
insurances[insur] = new LifeInsurance( pword, type, tm, year, month, day, pass, tc );
tc = insurances[insur].calculateCost( pass, a, insur );
insurances[insur].setCost( tc );
insur++;
}
System.out.println("y");
}
inp.close();
}
catch ( Exception e )
{
e.getMessage();
}
}
to read some info from a txt file and create an object of my LifeInsurance class or my CarInsurance. The reason i use this System.out.println("y"); is to see whether it completes as many loops as the objects i want to create. When i try to run my programm i only get 7 loops ( 7 y's ) but i need 13. The following is what i have wrote on the txt file :
1,12,2013,6,1,1939
2,12,2008,1,10,1939,Mercedes,E200,2000,AEX32100,false
2,12,2009,1,11,1939,Mercedes,E200,2000,AEX32100,false
2,12,2009,2,10,1940,Fiat,Punto,1400,BIO1245,false
2,12,2009,2,10,1940,Seat,Ibiza,1600,BIE3987,false
2,12,2010,1,10,1940,Seat,Ibiza,1600,BIE3987,false
2,12,2013,6,1,1941,Audi,A4,1600,IXE1256,true
1,12,2008,1,2,1942
1,12,2009,1,2,1942
1,12,2010,1,2,1942
1,12,2011,1,2,1943
2,12,2010,7,23,1943,Renault,Clio,1400,ZIO3890,true
2,12,2011,7,23,1943,Renault,Clio,1400,ZIO3890,true
Could you please help me out because i can't find a reason why it doesn't complete all 13 loops. Thank you in advance.
You should see the line 18 of your LifeInsurance class as suggests your stacktrace:
java.lang.NullPointerException at LifeInsurance.calculateCost(LifeInsurance.java:18)
The data of your 8th line seems to induce something null in the cost calculation.
In order to know if it is an error from inexpected kind of data or a problem in your code, you should first replace 1st line by your 8th and then replace arguments one by one if you don't want to try to debug your code.
(sorry for the "answer" it seems that i cannot add a remark)
I am assuming that you have all of your records in Insurances.txt on different lines as your data does have 13 distinct records there if one reads closely.
You should also follow what #jlordo is saying and change e.getMessage() to e.printStackTrace(). By doing that and running your code, I was getting
Having said that you should also trim all of your strings produced by your strLine.split(",") call. For example (without error checking) use this Integer.parseInt(array[0].trim()) instead of this Integer.parseInt(array[0]).
By doing the trim I was able to see the 'y' print 13 times.
You will never get 13 loops if your input file doesn't have 13 lines. Your program is expected to read them one line at a time.