001: /**********************************************************************************
002:
003: Feedzeo!
004: A free and open source RSS/Atom/RDF feed aggregator
005:
006: Copyright (C) 2005-2006 Anand Rao (anandrao@users.sourceforge.net)
007:
008: This library is free software; you can redistribute it and/or
009: modify it under the terms of the GNU Lesser General Public
010: License as published by the Free Software Foundation; either
011: version 2.1 of the License, or (at your option) any later version.
012:
013: This library is distributed in the hope that it will be useful,
014: but WITHOUT ANY WARRANTY; without even the implied warranty of
015: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016: Lesser General Public License for more details.
017:
018: You should have received a copy of the GNU Lesser General Public
019: License along with this library; if not, write to the Free Software
020: Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
021:
022: ************************************************************************************/package util;
023:
024: import java.io.*;
025: import java.util.*;
026:
027: class ItemCursor {
028:
029: public long Offset; /* file offset from which
030: the list of items for
031: a particular group starts*/
032:
033: /** Creates a new instance of FeedCursor */
034: public ItemCursor(long FileOffset) {
035: Offset = FileOffset;
036: }
037: }
038:
039: /*
040: *This class parsers a file in the following format:
041: * <Group name>
042: * {
043: * <Group item> [eg: <http://...]
044: * ....
045: * }
046: */
047:
048: class GroupInfo {
049: String GroupName; /* name of the group */
050: long ListOffset; /* offset from where the list of
051: * group items starts
052: */
053:
054: public GroupInfo(String Name, long Offset) {
055: GroupName = Name;
056: ListOffset = Offset;
057: }
058:
059: public String getGroupName() {
060: return GroupName;
061: }
062: }
063:
064: /**
065: *
066: * @author Anand Rao
067: */
068: public class ConfigFileParser extends RandomAccessFile {
069:
070: private final boolean DEBUG_CLASS = false;
071:
072: private void gotoFilePointer(long Offset) {
073: try {
074: seek(Offset);
075: } catch (Exception e) {
076: ExceptionUtil.reportException(e);
077: }
078: }
079:
080: private void commitFileChanges() {
081: try {
082: getChannel().force(false);
083: } catch (Exception e) {
084: ExceptionUtil.reportException(e);
085: }
086: }
087:
088: private void resetFilePointer() {
089: gotoFilePointer(0);
090: }
091:
092: private void gotoEndofFile() {
093: try {
094: gotoFilePointer(length());
095: } catch (Exception e) {
096: ExceptionUtil.reportException(e);
097: }
098: }
099:
100: private long getCurFilePointer() {
101: try {
102: long fp = getFilePointer();
103: return fp;
104: } catch (Exception e) {
105: ExceptionUtil.reportException(e);
106: return 0;
107: }
108: }
109:
110: private boolean IsEmpty(String s) {
111: for (int i = 0; i < s.length(); i++) {
112: if (s.charAt(i) == ' ')
113: continue;
114: else
115: return false;
116: }
117: return true;
118: }
119:
120: private String readNonBlankLine() {
121: try {
122: String line;
123: while (((line = readLine()) != null) && IsEmpty(line))
124: ; //Do nothing
125: if (line == null)
126: return null;
127: return line.trim(); // return line w/o whitespaces
128: } catch (Exception e) {
129: ExceptionUtil.reportException(e);
130: return null;
131: }
132: }
133:
134: /*
135: private String readLinewithoutComments() {
136: String s = readNonBlankLine();
137: if (s == null) return null;
138:
139: switch (CommentState) {
140: case NO_COMMENT: {
141: for (int i=0;i<s.length()-1;i=i+2) {
142: char fchar=s.char(i);
143: char schar=s.char(i+1);
144: if ((fchar == '/') && (schar == '*'))
145: CommentState= START_COMMENT;
146: else if ((fchar == '*') && (schar == '/'))
147: CommentState=NO_COMMENT;
148: else {
149: if (CommentState == NO_COMMENT) {
150: tmp.concat(String.valueOf(fchar)+String.valueOf(schar));
151: }
152: }
153: }
154: }
155:
156: case START_COMMENT: {
157: }
158: }
159: }
160:
161:
162: */
163:
164: /*
165: * writes a line to the config file
166: */
167: private void writeLine(String line) {
168:
169: try {
170: if (DEBUG_CLASS)
171: System.out.print("WRITELINE:");
172: for (int i = 0; i < line.length(); i++) {
173: // Note we use writeByte here instead of writeChar which will
174: // write the char in unicode 2 byte form; Using writeChar will
175: // not work as we use readLine() to read a line from the file
176: // which uses readByte();
177: //
178: char c = line.charAt(i);
179: writeByte(c);
180: System.out.print(c);
181: }
182: writeByte('\r');
183: System.out.print('\r');
184: writeByte('\n');
185: System.out.print('\n');
186:
187: /*
188: StringBuffer buf = new StringBuffer();
189: buf.append(line);
190: buf.append("\r\n");
191: System.out.println("WRITELINE:"+buf.toStr77ing());
192: writeBytes(buf.toString());*/
193: } catch (Exception e) {
194: ExceptionUtil.reportException(e);
195: }
196: }
197:
198: private long getFileLength() {
199: try {
200: long len = length();
201: return len;
202: } catch (Exception e) {
203: ExceptionUtil.reportException(e);
204: }
205: return 0;
206: }
207:
208: private void setFileLength(long len) {
209: try {
210: setLength(len);
211: } catch (Exception e) {
212: ExceptionUtil.reportException(e);
213: }
214: }
215:
216: private final String ITEMLST_START = "{";
217: private final String ITEMLST_END = "}";
218:
219: private final int STATE_GROUPSTART = 0;
220: private final int STATE_ITEMLSTSTART = 1;
221: private final int STATE_ITEMLIST = 2;
222:
223: private Vector GroupInfoColltn = null; /* Collection of groupinfo objects */
224: private ItemCursor ICursor = null;
225:
226: /*
227: * Maintaining group info allows us to quickly goto
228: * a particular group and start fetching the group items
229: * without having to search the file for the offset
230: */
231:
232: private void initGroupInfo() {
233: long fp = 0;
234: int state = STATE_GROUPSTART;
235: String line;
236: boolean Error = false;
237:
238: if (GroupInfoColltn != null)
239: GroupInfoColltn = null;
240: GroupInfoColltn = new Vector();
241:
242: while ((Error == false) && (line = readNonBlankLine()) != null) {
243: switch (state) {
244: case STATE_GROUPSTART: {
245: String tmp;
246: tmp = readNonBlankLine();
247: if (tmp.equalsIgnoreCase(ITEMLST_START)) {
248: /*
249: * file pointer now points to start of list
250: */
251: fp = getCurFilePointer();
252: GroupInfo gi = new GroupInfo(line, fp);
253: GroupInfoColltn.addElement(gi);
254: state = STATE_ITEMLSTSTART;
255: } else {
256: System.out.println("Parse error at input file "
257: + getCurFilePointer());
258: Error = true;
259: }
260: }
261: break;
262:
263: case STATE_ITEMLSTSTART:
264: if (line.equalsIgnoreCase(ITEMLST_END)) {
265: state = STATE_GROUPSTART;
266: }
267: //fp = getCurFilePointer();
268: break;
269: }
270: }
271:
272: if (state != STATE_GROUPSTART) {
273: System.out.println("Parse error at input file"
274: + getCurFilePointer());
275: }
276: }
277:
278: private long getGroupListOffset(String GroupName) {
279: for (int i = 0; i < GroupInfoColltn.size(); i++) {
280: GroupInfo gi = (GroupInfo) GroupInfoColltn.elementAt(i);
281: if (GroupName.equalsIgnoreCase(gi.GroupName)) {
282: return (gi.ListOffset);
283: }
284: }
285: return -1; //TODO; have proper error definations
286: }
287:
288: private long seektoGroupList(String GroupName) {
289: long Offset = getGroupListOffset(GroupName);
290: if (Offset >= 0)
291: gotoFilePointer(Offset);
292: return Offset;
293: }
294:
295: private void closeGroupCursor() {
296: ICursor = null;
297: }
298:
299: private void ReinitGroupInfo() {
300: resetFilePointer();
301: initFileParser();
302: }
303:
304: private void writeItem(String item) {
305: writeLine(item);
306: }
307:
308: private void writeCloseGroup() {
309: writeLine(ITEMLST_END);
310: }
311:
312: private void writeGroup(String Grpname, Vector ItemList) {
313: writeLine("\r\n");
314: writeLine(Grpname);
315: writeLine(ITEMLST_START);
316: for (int i = 0; i < ItemList.size(); i++) {
317: writeLine((String) ItemList.get(i));
318: }
319: writeLine(ITEMLST_END);
320: }
321:
322: private int insertNewGroup(String Grpname, String NewItem) {
323: /* goto end of file and add new group */
324: Vector v = new Vector();
325: v.addElement(NewItem);
326: gotoEndofFile();
327: writeGroup(Grpname, v);
328: setFileLength(getCurFilePointer());
329: commitFileChanges();
330: ReinitGroupInfo();
331: return 0;
332: }
333:
334: private void initFileParser() {
335: try {
336: initGroupInfo();
337: } catch (Exception e) {
338: ExceptionUtil.reportException(e);
339: }
340: resetFilePointer();
341: }
342:
343: private Vector fetchGroupContents(String grpname) {
344: if (initGroupCursor(grpname) < 0)
345: return null;
346: Vector contents = new Vector();
347: String item;
348: while ((item = getNextGroupItem(grpname)) != null) {
349: contents.addElement(item);
350: }
351: return contents;
352: }
353:
354: private boolean checkDuplicateGrpItem(String Grpname, String Item) {
355: String s = null;
356: if (initGroupCursor(Grpname) < 0)
357: return false;
358: while ((s = getNextGroupItem(Grpname)) != null) {
359: if (s.equalsIgnoreCase(Item))
360: return true;
361: }
362: return false;
363: }
364:
365: private boolean isItemPresentInGroup(String Grpname, String Item) {
366: return (checkDuplicateGrpItem(Grpname, Item));
367: }
368:
369: private void removeGroupItem(Vector grpItems, String Item) {
370: for (int i = 0; i < grpItems.size(); i++) {
371: String gItem = (String) grpItems.elementAt(i);
372: if (gItem.equalsIgnoreCase(Item)) {
373: grpItems.removeElement(gItem);
374: break;
375: }
376: }
377: }
378:
379: /////// PUBLIC FUNCTIONS
380:
381: /** Creates a new instance of ConfigFileParser */
382: public ConfigFileParser(String Path) throws FileNotFoundException {
383: // TODO : get exclusive lock over the file so that no two threads
384: // modify the file at the same time
385: super (Path, "rw");
386: initFileParser();
387: }
388:
389: public int getNumGroups() {
390: return GroupInfoColltn.size();
391: }
392:
393: public String getGroup(int index) {
394: GroupInfo gi = (GroupInfo) GroupInfoColltn.elementAt(index);
395: if (gi == null)
396: return null;
397: return gi.GroupName;
398: }
399:
400: public int initGroupCursor(String GroupName) {
401: long FileOffset = 0;
402:
403: if ((FileOffset = seektoGroupList(GroupName)) < 0)
404: return -1;
405: ICursor = new ItemCursor(FileOffset);
406: if (DEBUG_CLASS)
407: System.out.println("INITGROUPCURSOR:" + GroupName
408: + " cursor set at " + FileOffset);
409: return 0;
410: }
411:
412: public String getNextGroupItem(String GroupName) {
413: if (ICursor == null)
414: return null;
415: String tmp = readNonBlankLine();
416: if ((tmp != null) && (!tmp.equalsIgnoreCase(ITEMLST_END))) {
417: // update cursor offset
418: ICursor.Offset = getCurFilePointer();
419: return tmp;
420: } else {
421: closeGroupCursor();
422: return null; // we have reached end of the list
423: }
424: }
425:
426: public void CloseFile() {
427: try {
428: close();
429: } catch (Exception e) {
430: ExceptionUtil.reportException(e);
431: }
432: }
433:
434: public int insertGroupItem(String Grpname, String NewItem) {
435: if (initGroupCursor(Grpname) < 0) /* this is a new group */
436: return insertNewGroup(Grpname, NewItem);
437: else { /* group already exists */
438: /*
439: * first check if this item already exists in the group
440: */
441: if (checkDuplicateGrpItem(Grpname, NewItem)) {
442: System.out.println(" Dup item in grp=" + Grpname
443: + " item=" + NewItem);
444: return 0;
445: }
446: /*
447: * 1 - Form a list of groups coming after this group;
448: * 2 - Prefetch the contents of these groups;
449: * 3 - Write the new list item at the end of the group;
450: * 4 - Write the prefetched groups back to the file;
451: * 5 - Reinit the group info;
452: */
453: /* 1 */
454: Vector PrefetchList = new Vector();
455: int i;
456: for (i = 0; i < GroupInfoColltn.size(); i++) {
457: GroupInfo gi = (GroupInfo) GroupInfoColltn.elementAt(i);
458: String grpname = gi.getGroupName();
459: if (grpname.equalsIgnoreCase(Grpname))
460: break;
461: }
462: for (int j = (i + 1); j < GroupInfoColltn.size(); j++) {
463: GroupInfo gi = (GroupInfo) GroupInfoColltn.elementAt(j);
464: PrefetchList.addElement(gi.getGroupName());
465: }
466: /* 2 */
467: /* define a vector of vectors where each vector stores content
468: * coresponding to each group in the prefetch list
469: */
470: Vector PrefetchContent = new Vector();
471: for (int k = 0; k < PrefetchList.size(); k++) {
472: String Gname = (String) PrefetchList.elementAt(k);
473: PrefetchContent.addElement(fetchGroupContents(Gname));
474: }
475: /* 3 */
476: Vector ModifiedGroup = fetchGroupContents(Grpname);
477: /*
478: * Now we have the contents of the group fetched already, into which
479: * the new item is to be added;
480: * Goto the list offset of this group and write the items one by one
481: * finally writing the new list item; After this close this group
482: */
483: initGroupCursor(Grpname); /* takes file pointer to first list item */
484: for (int m = 0; m < ModifiedGroup.size(); m++) {
485: writeItem((String) ModifiedGroup.elementAt(m));
486: }
487: writeItem(NewItem);
488: writeCloseGroup();
489: /* 4 */
490: for (int p = 0; p < PrefetchList.size(); p++) {
491: writeGroup((String) PrefetchList.elementAt(p),
492: (Vector) PrefetchContent.elementAt(p));
493: }
494: setFileLength(getCurFilePointer());
495: // write the changes to the disk
496: commitFileChanges();
497: /* 5 */
498: ReinitGroupInfo();
499: return 0;
500: }
501: }
502:
503: public int deleteGroupItem(String Grpname, String Item) {
504: /*
505: * 1. check if the group exists; and if the Item exists inside the group
506: * 2. form a list of groups coming *after* this group and one group
507: * coming *before* this group
508: * 3. prefetch the contents of groups in step(2) and modified group
509: * 4. write the contents of group coming *before* this group
510: * 5. write the current group without the item,
511: * if this was the last item skip writing this group
512: * 6. writeback the prefetched groups
513: * 7. reinit group info
514: */
515:
516: /* 1 */
517: if (!isItemPresentInGroup(Grpname, Item)) /* the group doesn't exist or
518: item doesn't exist*/
519: return -1;
520:
521: /* 2 */
522: Vector PrefetchList = new Vector();
523: int i;
524: int indexOfGrpBeforeModifiedGroup = 0;
525:
526: for (i = 0; i < GroupInfoColltn.size(); i++) {
527: GroupInfo gi = (GroupInfo) GroupInfoColltn.elementAt(i);
528: String grpname = gi.getGroupName();
529: if (grpname.equalsIgnoreCase(Grpname)) {
530: indexOfGrpBeforeModifiedGroup = i - 1; // remember this group index
531: break;
532: }
533: }
534: for (int j = (i + 1); j < GroupInfoColltn.size(); j++) {
535: GroupInfo gi = (GroupInfo) GroupInfoColltn.elementAt(j);
536: PrefetchList.addElement(gi.getGroupName());
537: }
538:
539: /* 3 */
540: /* define a vector of vectors where each vector stores content
541: * coresponding to each group in the prefetch list
542: */
543: Vector PrefetchContent = new Vector();
544: for (int k = 0; k < PrefetchList.size(); k++) {
545: String Gname = (String) PrefetchList.elementAt(k);
546: PrefetchContent.addElement(fetchGroupContents(Gname));
547: }
548: // prefetch contents of modified group (group from which the item is to be deleted )
549: Vector ModifiedGroup = fetchGroupContents(Grpname);
550:
551: /* 4 */
552: // write the contents of group coming before the modified group;
553: // check for valid index, since there may not be any group before modified group
554: // if it is the first group in the file;
555: // now we start writing to the file, so order is important
556: if (indexOfGrpBeforeModifiedGroup >= 0) { // there is a valid group before modified grp
557: GroupInfo gbfModifiedGroup = (GroupInfo) GroupInfoColltn
558: .elementAt(indexOfGrpBeforeModifiedGroup);
559: String gbfModifiedGroupName = gbfModifiedGroup
560: .getGroupName();
561: Vector gbfModifiedGroupItems = fetchGroupContents(gbfModifiedGroupName);
562:
563: // start write into the file
564: initGroupCursor(gbfModifiedGroupName); /* takes file pointer to 1st list item */
565: for (int m = 0; m < gbfModifiedGroupItems.size(); m++) {
566: writeItem((String) gbfModifiedGroupItems.elementAt(m));
567: }
568: writeCloseGroup();
569: } else
570: resetFilePointer(); // since modified group is the 1st grp in the file
571:
572: // At this point, the file pointer is set correctly to write the modified
573: // group without the item to be deleted or to skip writing the modified group if it
574: // is last item deleted in the group
575:
576: /* 5 */
577: if (ModifiedGroup.size() > 1) {
578: removeGroupItem(ModifiedGroup, Item);
579: writeGroup(Grpname, ModifiedGroup);
580: }
581:
582: /* 6 */
583: for (int p = 0; p < PrefetchList.size(); p++) {
584: writeGroup((String) PrefetchList.elementAt(p),
585: (Vector) PrefetchContent.elementAt(p));
586: }
587:
588: setFileLength(getCurFilePointer());
589: // write the changes to the disk
590: commitFileChanges();
591: /* 7 */
592: ReinitGroupInfo();
593:
594: return 0;
595: }
596:
597: }
|