Skip to content

Commit 271ca20

Browse files
committed
New Clippings parser added for Older Kindles.
* UI: Option to select a parser. * UI: A info button, shows general information on the selected parser. * ParsingErrors enum is removed, it was a bad design in Java. Now parseLine function throws exception if it detects a parsing error.
1 parent 8ac76a2 commit 271ca20

File tree

7 files changed

+350
-127
lines changed

7 files changed

+350
-127
lines changed

src/coderarjob/kpdfsync/lib/clipparser/AbstractParser.java

Lines changed: 7 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,16 @@
77
import java.io.FileNotFoundException;
88

99
import coderarjob.ajl.file.ByteOrderMarkTypes;
10-
import coderarjob.kpdfsync.lib.clipparser.ParserResult.SupportedFields;
1110
import coderarjob.ajl.file.ByteOrderMark;
1211

1312
public abstract class AbstractParser
1413
{
15-
protected enum ParsingErrors
16-
{
17-
NO_ERROR, END_OF_BLOCK_REACHED, PARSING_ERROR;
18-
19-
private String mTag;
20-
public void setTag (String name) { this.mTag = name; }
21-
public String getTag() { return this.mTag; }
22-
23-
}
24-
2514
/* Abstract public methods */
2615
public abstract String getParserVersion();
2716
public abstract String[] getSupportedKindleVersions();
2817

2918
/* Abstract protected methods */
30-
protected abstract ParsingErrors parseLine(int linei, ParserResult res) throws Exception;
19+
protected abstract boolean parseLine(int linei, ParserResult res) throws Exception;
3120
protected abstract AbstractKindleParserConstants getKindleParserConstants();
3221

3322
/* Protected fields */
@@ -154,7 +143,6 @@ protected Charset getCharsetFromByteOrderMarkType (ByteOrderMarkTypes type)
154143
*/
155144
public ParserResult parse() throws Exception
156145
{
157-
ParsingErrors parseError = ParsingErrors.NO_ERROR;
158146
ParserResult result = new ParserResult();
159147

160148
/* End of file was reached before */
@@ -168,12 +156,13 @@ public ParserResult parse() throws Exception
168156
{
169157
onParsingStart();
170158

171-
for (int i = 0; parseError == ParsingErrors.NO_ERROR; i++)
159+
boolean isTerminationLineReached = false;
160+
for (int i = 0; isTerminationLineReached == false; i++)
172161
{
173162
if (Thread.interrupted() == true)
174163
throw new InterruptedException();
175164

176-
parseError = parseLine(i, result);
165+
isTerminationLineReached = parseLine(i, result);
177166
}
178167

179168
} catch (InterruptedException ex) {
@@ -184,27 +173,17 @@ public ParserResult parse() throws Exception
184173
throw ex;
185174
}
186175

187-
/* Parsing failed at some point*/
188-
if (parseError == ParsingErrors.PARSING_ERROR)
189-
{
190-
String errDes = String.format ("Parsing error: '%s' is not '%s'.",
191-
(isEOF() == true) ? "<EOF>" : this.lastLineRead(),
192-
parseError.getTag());
193-
onParsingError(errDes, result);
194-
throw new ParserException (errDes);
195-
}
196-
197176
onParsingSuccess (result);
198177
return result;
199178
}
200179

201180
/** Generates a Parser Exception object for subclasses to use.
202181
* This ensures a consistent Exception description.
203182
*/
204-
protected ParserException genParserException (String stage)
183+
protected ParserException genParserException (String field)
205184
{
206-
String errDes = String.format ("Parsing error: '%s' is not '%s'.",
207-
this.lastLineRead(), stage);
185+
String errDes = String.format ("Invalid '%s' in line '%s'.",
186+
field, this.lastLineRead());
208187
return new ParserException (errDes);
209188
}
210189

src/coderarjob/kpdfsync/lib/clipparser/KindleParserV1.java

Lines changed: 58 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,37 @@
1818

1919
public class KindleParserV1 extends AbstractParser
2020
{
21+
public enum ParsingStages
22+
{
23+
TITLE("Title"),
24+
FILE_OFFSET("Annotation block offset in file"),
25+
ANNOTATION_TYPE("Annotation Type"),
26+
PAGE_OR_LOCATION_NUMBER("Page or location number"),
27+
PAGE_NUMBER_TYPE("Page number type"),
28+
TEXT("Text"),
29+
END_OF_BLOCK("End of Block");
30+
31+
private final String _name;
32+
public String getName() { return _name; }
33+
private ParsingStages (String name) { _name = name; }
34+
}
35+
2136
public KindleParserV1 (String fileName) throws FileNotFoundException, IOException
2237
{
2338
/* Clippings file is opened and onClippingsFileOpen hook method is called. */
2439
super (fileName);
2540
}
2641

42+
public static String getParserName()
43+
{
44+
return "Kindle Clippings - Newer kindles";
45+
}
46+
47+
public String toString()
48+
{
49+
return KindleParserV1.getParserName();
50+
}
51+
2752
/* Implementing abstract methods from AbstractParser*/
2853
protected AbstractKindleParserConstants getKindleParserConstants ()
2954
{
@@ -44,84 +69,71 @@ public String getParserVersion ()
4469

4570
public String[] getSupportedKindleVersions ()
4671
{
47-
return new String[] {"1.2.4", "1.2.5", "1.2.6"};
72+
return new String[] {"5.12.*", "newer"};
4873
}
4974

5075
/**
5176
* Parses each line of the current block.
5277
* Returns a ParserResult object with the parsed result.
53-
* Null is returned is EOF was reached.
78+
* Returns True, if there reached end of the block. False otherwise.
5479
*/
55-
protected ParsingErrors parseLine(int lineIndex, ParserResult result)
80+
protected boolean parseLine(int lineIndex, ParserResult result)
5681
throws IOException, ParserException
5782
{
58-
ParsingErrors err = ParsingErrors.NO_ERROR;
59-
String lineStr = null;
60-
6183
switch (lineIndex)
6284
{
6385
case 0 :
6486
this.readLineWithProperEncoding();
65-
err = parseTitleLine (result);
66-
err.setTag ("Title line");
87+
parseTitleLine (result);
6788
break;
6889
case 1 :
6990
this.readLineWithProperEncoding();
70-
err = parseAnnotationLine (result);
71-
err.setTag ("Annotation line");
91+
parseAnnotationLine (result);
7292
break;
7393
case 2 :
7494
this.readLineWithProperEncoding();
75-
err = parseTextLine (result);
76-
err.setTag ("Text line");
95+
parseTextLine (result);
7796
break;
7897
case 3 :
7998
this.readLineWithProperEncoding();
80-
err = parseTerminationLine (result);
81-
err.setTag ("Termination line");
82-
break;
99+
parseTerminationLine (result);
100+
return true;
83101
default:
84-
err =ParsingErrors.END_OF_BLOCK_REACHED;
85-
break;
102+
String desc = String.format ("At an invalid line. Line #%d", lineIndex);
103+
throw new ParserException (desc);
86104
}
87-
88-
return err;
105+
return false;
89106
}
90107

91108
/* Class methods */
92109

93110
/**
94-
* Validates Book Title line and adds to ParserResult and returns true is valid.
95-
* If validation fails, false is returned.
111+
* Validates Book Title line and adds to ParserResult.
96112
*/
97-
protected ParsingErrors parseTitleLine (ParserResult result)
98-
throws IOException, ParserException
113+
protected void parseTitleLine (ParserResult result) throws IOException, ParserException
99114
{
100115
/* Read current line. Cannot be EOF.*/
101116
String linestr = this.lastLineRead();
102117
if (linestr == null)
103-
return ParsingErrors.PARSING_ERROR;
118+
throw genParserException (ParsingStages.TITLE.getName());
104119

105120
boolean isValid = (linestr.length () > 0);
106121
if (isValid == false)
107-
return ParsingErrors.PARSING_ERROR;
122+
throw genParserException (ParsingStages.TITLE.getName());
108123

109124
result.setFieldValue (SupportedFields.TITLE, linestr.trim());
110125
result.setFieldValue (SupportedFields.FILE_OFFSET, String.valueOf(this.lastFilePointer()));
111-
return ParsingErrors.NO_ERROR;
112126
}
113127

114128
/**
115-
* Validates Book Annotation type line and adds to ParserResult and returns true is valid.
116-
* If validation fails, false is returned.
129+
* Validates Book Annotation type line and adds to ParserResult.
117130
*/
118-
protected ParsingErrors parseAnnotationLine (ParserResult result)
119-
throws IOException, ParserException
131+
protected void parseAnnotationLine (ParserResult result) throws IOException, ParserException
120132
{
121133
/* Read current line. Cannot be EOF.*/
122134
String linestr = this.lastLineRead();
123135
if (linestr == null)
124-
return ParsingErrors.PARSING_ERROR;
136+
throw genParserException (ParsingStages.ANNOTATION_TYPE.getName());
125137

126138
boolean isValid = false;
127139
String value = "";
@@ -134,7 +146,7 @@ protected ParsingErrors parseAnnotationLine (ParserResult result)
134146
|| value.toLowerCase().equals ("bookmark"));
135147

136148
if (isValid == false)
137-
return ParsingErrors.PARSING_ERROR;
149+
throw genParserException (ParsingStages.ANNOTATION_TYPE.getName());
138150

139151
result.setFieldValue (SupportedFields.ANNOTATION_TYPE, value);
140152
String annotationType = value;
@@ -146,50 +158,48 @@ protected ParsingErrors parseAnnotationLine (ParserResult result)
146158
|| value.toLowerCase().equals("location"));
147159

148160
if (isValid == false)
149-
return ParsingErrors.PARSING_ERROR;
161+
throw genParserException (ParsingStages.PAGE_NUMBER_TYPE.getName());
150162

151163
result.setFieldValue (SupportedFields.PAGE_NUMBER_TYPE, value);
152164

153165
/* Page or Location Number */
154166
value = trySplitString (linestr, " ", mConstants.getAnnotationLinePageOrLocationNumberPosition());
155167
isValid = (value != null);
156168
if (isValid == false)
157-
return ParsingErrors.PARSING_ERROR;
169+
throw genParserException (ParsingStages.PAGE_OR_LOCATION_NUMBER.getName());
158170

159171
if (annotationType.toLowerCase().equals("bookmark") == false)
160172
{
161173
value = trySplitString (value, "-", 0);
162174
isValid = (value != null);
163175
if (isValid == false)
164-
return ParsingErrors.PARSING_ERROR;
176+
throw genParserException (ParsingStages.PAGE_OR_LOCATION_NUMBER.getName());
165177
}
166178

167179
isValid = tryParseUnsigendInt (value);
168180
if (isValid == false)
169-
return ParsingErrors.PARSING_ERROR;
181+
throw genParserException (ParsingStages.PAGE_OR_LOCATION_NUMBER.getName());
170182

171183
result.setFieldValue (SupportedFields.PAGE_OR_LOCATION_NUMBER, value);
172-
return ParsingErrors.NO_ERROR;
173184
}
174185

175186
/**
176-
* Validates Book highlight/note text line and adds to ParserResult and returns true is valid.
177-
* If validation fails, false is returned.
187+
* Validates Book highlight/note text line and adds to ParserResult.
178188
*/
179-
protected ParsingErrors parseTextLine (ParserResult result) throws IOException, ParserException
189+
protected void parseTextLine (ParserResult result) throws IOException, ParserException
180190
{
181191
/* Read current line. Cannot be EOF.*/
182192
String linestr = this.lastLineRead();
183193
if (linestr == null)
184-
return ParsingErrors.PARSING_ERROR;
194+
throw genParserException (ParsingStages.TEXT.getName());
185195

186196
boolean isValid = false;
187197
String annotationType = result.getFieldValue(SupportedFields.ANNOTATION_TYPE).toLowerCase();
188198

189199
/* There should be a blank line */
190200
isValid = (linestr.length() == 0);
191201
if (isValid == false)
192-
return ParsingErrors.PARSING_ERROR;
202+
throw genParserException (ParsingStages.TEXT.getName());
193203

194204
/* Read the actual text, in the following lines */
195205
StringBuilder sb = new StringBuilder();
@@ -202,7 +212,7 @@ protected ParsingErrors parseTextLine (ParserResult result) throws IOException,
202212
{
203213
isValid = (linestr.length() > 0);
204214
if (isValid == false)
205-
return ParsingErrors.PARSING_ERROR;
215+
throw genParserException (ParsingStages.TEXT.getName());
206216
}
207217
}
208218

@@ -213,24 +223,22 @@ protected ParsingErrors parseTextLine (ParserResult result) throws IOException,
213223
sb.deleteCharAt(sb.length() - 1);
214224

215225
result.setFieldValue (SupportedFields.TEXT, sb.toString());
216-
return ParsingErrors.NO_ERROR;
217226
}
218227

219228
/**
220-
* Validates entry end/termination line and adds to ParserResult and returns true is valid.
221-
* If validation fails, false is returned.
229+
* Validates entry end/termination line.
222230
*/
223-
protected ParsingErrors parseTerminationLine (ParserResult result)
224-
throws IOException, ParserException
231+
protected void parseTerminationLine (ParserResult result) throws IOException, ParserException
225232
{
226233
/* Read current line. Cannot be EOF.*/
227234
String linestr = this.lastLineRead();
228235
if (linestr == null)
229-
return ParsingErrors.PARSING_ERROR;
236+
throw genParserException (ParsingStages.END_OF_BLOCK.getName());
230237

231238
/* Check for termination line. */
232239
boolean isValid = isTerminationLine (linestr);
233-
return (isValid == true) ? ParsingErrors.NO_ERROR : ParsingErrors.PARSING_ERROR;
240+
if (isValid == false)
241+
throw genParserException (ParsingStages.END_OF_BLOCK.getName());
234242
}
235243

236244
protected String trySplitString (String s, String p, int index)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* KindleParserV2 Class
3+
*
4+
* Based on KindleParserV1, but minor change in Annotation line parsing.
5+
*
6+
* Dated: 11 July 2022
7+
* Author: arjobmukherjee@gmail.com
8+
*/
9+
10+
package coderarjob.kpdfsync.lib.clipparser;
11+
12+
import java.io.FileNotFoundException;
13+
import java.io.IOException;
14+
15+
public class KindleParserV2 extends KindleParserV1
16+
{
17+
public KindleParserV2 (String fileName) throws FileNotFoundException, IOException
18+
{
19+
super (fileName);
20+
}
21+
22+
public static String getParserName()
23+
{
24+
return "Kindle Clippings - Older kindles";
25+
}
26+
27+
public String toString()
28+
{
29+
return KindleParserV2.getParserName();
30+
}
31+
32+
/* Implementing abstract methods from AbstractParser*/
33+
public String getParserVersion ()
34+
{
35+
return "2.0";
36+
}
37+
38+
public String[] getSupportedKindleVersions ()
39+
{
40+
return new String[] {"3.4.3", "older"};
41+
}
42+
43+
protected AbstractKindleParserConstants getKindleParserConstants ()
44+
{
45+
AbstractKindleParserConstants constants = new AbstractKindleParserConstants () {
46+
public int getAnnotationLineTypePosition() { return 1; }
47+
public int getAnnotationLinePageNumberTypePosition() { return 3; }
48+
public int getAnnotationLinePageOrLocationNumberPosition() { return 4; }
49+
public String getTeminationLinePattern () { return "=========="; }
50+
};
51+
52+
return constants;
53+
}
54+
}

0 commit comments

Comments
 (0)