1:
38:
39: package ;
40:
41: import ;
42: import ;
43: import ;
44: import ;
45: import ;
46: import ;
47: import ;
48: import ;
49: import ;
50: import ;
51: import ;
52: import ;
53: import ;
54: import ;
55:
56:
61: public class FileNewsrc
62: implements Newsrc
63: {
64:
65: private static final String NEWSRC_ENCODING = "US-ASCII";
66:
67: protected File file;
68:
69: protected List subs = null;
70: protected List groups = null;
71: protected Map lines = null;
72: protected boolean dirty;
73: protected boolean debug;
74:
75:
80: public FileNewsrc(File file, boolean debug)
81: {
82: this.file = file;
83: this.debug = debug;
84: }
85:
86: public void close()
87: {
88: if (!dirty)
89: {
90: return;
91: }
92: save();
93: }
94:
95:
98: void load()
99: {
100: long fs = file.length();
101: long max = (long) Integer.MAX_VALUE;
102: int bs = (int) (fs > max ? max : fs);
103:
104: groups = new LinkedList();
105: lines = new HashMap(bs / 20);
106: subs = new LinkedList();
107:
108:
109: try
110: {
111: long t1 = System.currentTimeMillis();
112: if (debug)
113: {
114: System.err.println("DEBUG: nntp: newsrc loading " +
115: file.getPath());
116: }
117:
118: FileInputStream fr = new FileInputStream(file);
119: InputStreamReader ir = new InputStreamReader(fr, NEWSRC_ENCODING);
120: BufferedReader reader = new BufferedReader(ir, bs);
121: String line = reader.readLine();
122: while (line != null)
123: {
124: int cp = line.indexOf(':');
125: if (cp > -1)
126: {
127:
128: String name = line.substring(0, cp);
129: groups.add(name);
130: subs.add(name);
131: cp++;
132: if (cp < line.length())
133: {
134: String tail = line.substring(cp).trim();
135: if (tail.length() > 0)
136: {
137: lines.put(name, tail);
138: }
139: }
140: }
141: else
142: {
143: int pp = line.indexOf('!');
144: if (pp > -1)
145: {
146:
147: String name = line.substring(0, pp);
148: groups.add(name);
149: pp++;
150: if (pp < line.length())
151: {
152: String tail = line.substring(pp).trim();
153: if (tail.length() > 0)
154: {
155: lines.put(name, tail);
156: }
157: }
158: }
159:
160: }
161: line = reader.readLine();
162: }
163: reader.close();
164: long t2 = System.currentTimeMillis();
165: if (debug)
166: {
167: System.err.println("DEBUG: nntp: newsrc load: " +
168: groups.size() + " groups in " +
169: (t2 - t1) + "ms");
170: }
171: }
172: catch (FileNotFoundException e)
173: {
174: }
175: catch (IOException e)
176: {
177: System.err.println("WARNING: nntp: unable to read newsrc file");
178: if (debug)
179: {
180: e.printStackTrace(System.err);
181: }
182: }
183: catch (SecurityException e)
184: {
185: System.err.println("WARNING: nntp: " +
186: "no read permission on newsrc file");
187: }
188: dirty = false;
189: }
190:
191:
194: void save()
195: {
196: try
197: {
198: long t1 = System.currentTimeMillis();
199: if (debug)
200: {
201: System.err.println("DEBUG: nntp: newsrc saving " +
202: file.getPath());
203: }
204:
205: int bs = (groups.size() * 20);
206: FileOutputStream fw = new FileOutputStream(file);
207: BufferedOutputStream writer = new BufferedOutputStream(fw, bs);
208: for (Iterator i = groups.iterator(); i.hasNext();)
209: {
210: String group = (String) i.next();
211: StringBuffer buffer = new StringBuffer(group);
212: if (subs.contains(group))
213: {
214: buffer.append(':');
215: }
216: else
217: {
218: buffer.append('!');
219: }
220: Object r = lines.get(group);
221: if (r instanceof String)
222: {
223: buffer.append((String) r);
224: }
225: else
226: {
227: RangeList ranges = (RangeList) r;
228: if (ranges != null)
229: {
230: buffer.append(ranges.toString());
231: }
232: }
233: buffer.append('\n');
234:
235: byte[] bytes = buffer.toString().getBytes(NEWSRC_ENCODING);
236: writer.write(bytes);
237: }
238: writer.flush();
239: writer.close();
240:
241: long t2 = System.currentTimeMillis();
242: if (debug)
243: {
244: System.err.println("DEBUG: nntp: newsrc save: " +
245: groups.size() + " groups in " +
246: (t2 - t1) + "ms");
247: }
248: }
249: catch (IOException e)
250: {
251: System.err.println("WARNING: nntp: unable to save newsrc file");
252: if (debug)
253: {
254: e.printStackTrace(System.err);
255: }
256: }
257: dirty = false;
258: }
259:
260:
264: public Iterator list()
265: {
266: if (subs == null)
267: {
268: load();
269: }
270: return subs.iterator();
271: }
272:
273: public boolean isSubscribed(String newsgroup)
274: {
275: if (subs == null)
276: {
277: load();
278: }
279: return (subs.contains(newsgroup));
280: }
281:
282: public void setSubscribed(String newsgroup, boolean flag)
283: {
284: if (subs == null)
285: {
286: load();
287: }
288: if (flag && !groups.contains(newsgroup))
289: {
290: groups.add(newsgroup);
291: }
292: boolean subscribed = subs.contains(newsgroup);
293: if (flag && !subscribed)
294: {
295: subs.add(newsgroup);
296: dirty = true;
297: }
298: else if (!flag && subscribed)
299: {
300: subs.remove(newsgroup);
301: dirty = true;
302: }
303: }
304:
305: public boolean isSeen(String newsgroup, int article)
306: {
307: if (subs == null)
308: {
309: load();
310: }
311: Object value = lines.get(newsgroup);
312: if (value instanceof String)
313: {
314: value = new RangeList((String) value);
315: }
316: RangeList ranges = (RangeList) value;
317: if (ranges != null)
318: {
319: return ranges.isSeen(article);
320: }
321: return false;
322: }
323:
324: public void setSeen(String newsgroup, int article, boolean flag)
325: {
326: if (subs == null)
327: {
328: load();
329: }
330: Object value = lines.get(newsgroup);
331: if (value instanceof String)
332: {
333: value = new RangeList((String) value);
334: }
335: RangeList ranges = (RangeList) value;
336: if (ranges == null)
337: {
338: ranges = new RangeList();
339: lines.put(newsgroup, ranges);
340: dirty = true;
341: }
342: if (ranges.isSeen(article) != flag)
343: {
344: ranges.setSeen(article, flag);
345: dirty = true;
346: }
347: }
348:
349:
353: static class RangeList
354: {
355:
356: List seen;
357:
358: RangeList()
359: {
360: seen = new ArrayList();
361: }
362:
363: RangeList(String line)
364: {
365: this();
366: try
367: {
368:
369: int start = 0;
370: int end = line.indexOf(',');
371: while (end > start)
372: {
373: String token = line.substring(start, end);
374: addToken(token);
375: start = end + 1;
376: end = line.indexOf(',', start);
377: }
378: addToken(line.substring(start));
379: }
380: catch (NumberFormatException e)
381: {
382: System.err.println("ERROR: nntp: bad newsrc format: " + line);
383: }
384: }
385:
386:
389: private void addToken(String token) throws NumberFormatException
390: {
391: int hp = token.indexOf('-');
392: if (hp > -1)
393: {
394:
395: String fs = token.substring(0, hp);
396: String ts = token.substring(hp + 1);
397: int from = Integer.parseInt(fs);
398: int to = Integer.parseInt(ts);
399: if (from > -1 && to > -1)
400: {
401: insert(from, to);
402: }
403: }
404: else
405: {
406:
407: int number = Integer.parseInt(token);
408: if (number > -1)
409: {
410: insert(number);
411: }
412: }
413: }
414:
415:
418: public boolean isSeen(int num)
419: {
420: int len = seen.size();
421: Range[] r = new Range[len];
422: seen.toArray(r);
423: for (int i = 0; i < len; i++)
424: {
425: if (r[i].contains(num))
426: {
427: return true;
428: }
429: }
430: return false;
431: }
432:
433:
436: public void setSeen(int num, boolean flag)
437: {
438: if (flag)
439: {
440: insert(num);
441: }
442: else
443: {
444: remove(num);
445: }
446: }
447:
448:
452: int indexOf(int num)
453: {
454: int len = seen.size();
455: Range[] r = new Range[len];
456: seen.toArray(r);
457: for (int i = 0; i < len; i++)
458: {
459: if (r[i].contains(num))
460: {
461: return i;
462: }
463: if (r[i].from > num)
464: {
465: return i;
466: }
467: if (r[i].to == num - 1)
468: {
469: return i;
470: }
471: }
472: return len;
473: }
474:
475: void insert(int start, int end)
476: {
477: Range range = new Range(start, end);
478: int i1 = indexOf(range.from);
479:
480: if (i1 == seen.size())
481: {
482: seen.add(range);
483: return;
484: }
485: Range r1 = (Range) seen.get(i1);
486:
487: if (range.to < r1.from)
488: {
489: seen.add(i1, range);
490: return;
491: }
492:
493: if (r1.from <= range.from && r1.to >= range.to)
494: {
495: return;
496: }
497:
498: int i2 = indexOf(range.to);
499: Range r2 = (Range) seen.get(i2);
500: System.err.println("r2 " + r2 + " i2 " + i2);
501:
502: for (int i = i2; i >= i1; i--)
503: {
504: seen.remove(i);
505: }
506:
507: int f = (range.from < r1.from) ? range.from : r1.from;
508: int t = (range.to > r2.to) ? range.to : r2.to;
509: range = new Range(f, t);
510: seen.add(i1, range);
511: }
512:
513: void insert(int num)
514: {
515: insert(num, num);
516: }
517:
518: void remove(int num)
519: {
520: int i = indexOf(num);
521: Range r = (Range) seen.get(i);
522: seen.remove(i);
523:
524: if ((r.from == r.to) &&(r.to == num))
525: {
526: return;
527: }
528:
529: if (r.to > num)
530: {
531: Range r2 = new Range(num + 1, r.to);
532: seen.add(i, r2);
533: }
534: if (r.from < num)
535: {
536: Range r2 = new Range(r.from, num - 1);
537: seen.add(i, r2);
538: }
539: }
540:
541: public String toString()
542: {
543: StringBuffer buf = new StringBuffer();
544: int len = seen.size();
545: for (int i = 0; i < len; i++)
546: {
547: Range range = (Range) seen.get(i);
548: if (i > 0)
549: {
550: buf.append(',');
551: }
552: buf.append(range.toString());
553: }
554: return buf.toString();
555: }
556:
557: }
558:
559:
562: static class Range
563: {
564: int from;
565: int to;
566:
567: public Range(int i)
568: {
569: from = to = i;
570: }
571:
572: public Range(int f, int t)
573: {
574: if (f > t)
575: {
576: from = t;
577: to = f;
578: }
579: else
580: {
581: from = f;
582: to = t;
583: }
584: }
585:
586: public boolean contains(int num)
587: {
588: return (num >= from && num <= to);
589: }
590:
591: public String toString()
592: {
593: if (from != to)
594: {
595: return new StringBuffer()
596: .append(from)
597: .append('-')
598: .append(to)
599: .toString();
600: }
601: else
602: {
603: return Integer.toString(from);
604: }
605: }
606:
607: }
608:
609: }