forked from I2P_Developers/i2p.i2p
News: new NewsManager to maintain current news entries.
WIP, not yet hooked in.
This commit is contained in:
242
apps/routerconsole/java/src/net/i2p/router/news/NewsManager.java
Normal file
242
apps/routerconsole/java/src/net/i2p/router/news/NewsManager.java
Normal file
@ -0,0 +1,242 @@
|
||||
package net.i2p.router.news;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.app.ClientAppManager;
|
||||
import net.i2p.app.ClientAppState;
|
||||
import static net.i2p.app.ClientAppState.*;
|
||||
import net.i2p.router.app.RouterApp;
|
||||
import net.i2p.util.FileUtil;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.TranslateReader;
|
||||
|
||||
import org.cybergarage.xml.Node;
|
||||
|
||||
/**
|
||||
* Manage current news.
|
||||
* Keeps current entries in memory, and provide methods to
|
||||
* add new entries and store them to disk.
|
||||
*
|
||||
* @since 0.9.23
|
||||
*/
|
||||
public class NewsManager implements RouterApp {
|
||||
|
||||
private final I2PAppContext _context;
|
||||
private final Log _log;
|
||||
private final ClientAppManager _cmgr;
|
||||
private volatile ClientAppState _state = UNINITIALIZED;
|
||||
private List<NewsEntry> _currentNews;
|
||||
private NewsMetadata _currentMetadata;
|
||||
|
||||
public static final String APP_NAME = "news";
|
||||
private static final String BUNDLE_NAME = "net.i2p.router.news.messages";
|
||||
|
||||
/**
|
||||
* @param args ignored
|
||||
*/
|
||||
public NewsManager(I2PAppContext ctx, ClientAppManager listener, String[] args) {
|
||||
_context = ctx;
|
||||
_cmgr = listener;
|
||||
_log = ctx.logManager().getLog(NewsManager.class);
|
||||
_state = INITIALIZED;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return non-null, sorted by updated date, newest first
|
||||
*/
|
||||
public synchronized List<NewsEntry> getEntries() {
|
||||
if (!_currentNews.isEmpty())
|
||||
return new ArrayList(_currentNews);
|
||||
// load old news.xml
|
||||
List<NewsEntry> rv = parseOldNews();
|
||||
if (!rv.isEmpty()) {
|
||||
_currentNews = rv;
|
||||
// don't save to disk as we don't have the UUIDs so they will be dups ??
|
||||
return rv;
|
||||
}
|
||||
// load and translate initialnews
|
||||
// We don't save it to _currentNews, as the language may change
|
||||
return parseInitialNews();
|
||||
}
|
||||
|
||||
/**
|
||||
* Store each entry.
|
||||
* Old entries are always overwritten, as they may change even without the updated date changing.
|
||||
* Does NOT update the NewsEntry list.
|
||||
*
|
||||
* @param entries each one should be "entry" at the root
|
||||
* @return success
|
||||
*/
|
||||
public synchronized boolean storeEntries(List<Node> entries) {
|
||||
return PersistNews.store(_context, entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add or replace each entry in the list.
|
||||
* Does NOT store them to disk.
|
||||
*/
|
||||
public synchronized void addEntries(List<NewsEntry> entries) {
|
||||
for (NewsEntry e : entries) {
|
||||
String id = e.id;
|
||||
if (id == null)
|
||||
continue;
|
||||
boolean found = false;
|
||||
for (int i = 0; i < _currentNews.size(); i++) {
|
||||
NewsEntry old = _currentNews.get(i);
|
||||
if (id.equals(old.id)) {
|
||||
_currentNews.set(i, e);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
_currentNews.add(e);
|
||||
}
|
||||
Collections.sort(_currentNews);
|
||||
}
|
||||
|
||||
/////// ClientApp methods
|
||||
|
||||
/**
|
||||
* ClientApp interface
|
||||
*/
|
||||
public synchronized void startup() {
|
||||
changeState(STARTING);
|
||||
_currentNews = PersistNews.load(_context);
|
||||
changeState(RUNNING);
|
||||
if (_cmgr != null)
|
||||
_cmgr.register(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* ClientApp interface
|
||||
* @param args ignored
|
||||
*/
|
||||
public synchronized void shutdown(String[] args) {
|
||||
changeState(STOPPED);
|
||||
}
|
||||
|
||||
public ClientAppState getState() {
|
||||
return _state;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return APP_NAME;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return "News Manager";
|
||||
}
|
||||
|
||||
/////// end ClientApp methods
|
||||
|
||||
private synchronized void changeState(ClientAppState state) {
|
||||
_state = state;
|
||||
if (_cmgr != null)
|
||||
_cmgr.notify(this, state, null, null);
|
||||
}
|
||||
|
||||
private List<NewsEntry> parseOldNews() {
|
||||
File file = new File(_context.getConfigDir(), "docs/news.xml");
|
||||
String newsContent = FileUtil.readTextFile(file.toString(), -1, true);
|
||||
if (newsContent == null || newsContent.equals(""))
|
||||
return Collections.emptyList();
|
||||
return parseNews(newsContent);
|
||||
}
|
||||
|
||||
private List<NewsEntry> parseInitialNews() {
|
||||
NewsEntry entry = new NewsEntry();
|
||||
File file = new File(_context.getBaseDir(), "docs/initialNews/initialNews.xml");
|
||||
Reader reader = null;
|
||||
try {
|
||||
char[] buf = new char[512];
|
||||
StringBuilder out = new StringBuilder(2048);
|
||||
reader = new TranslateReader(_context, BUNDLE_NAME, new FileInputStream(file));
|
||||
int len;
|
||||
while((len = reader.read(buf)) > 0) {
|
||||
out.append(buf, 0, len);
|
||||
}
|
||||
List<NewsEntry> rv = parseNews(out.toString());
|
||||
if (!rv.isEmpty()) {
|
||||
rv.get(0).updated = RFC3339Date.parse3339Date("2015-01-01");
|
||||
} else {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("failed to load " + file);
|
||||
}
|
||||
return rv;
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("failed to load " + file, ioe);
|
||||
return Collections.emptyList();
|
||||
} finally {
|
||||
try {
|
||||
if (reader != null)
|
||||
reader.close();
|
||||
} catch (IOException foo) {}
|
||||
}
|
||||
}
|
||||
|
||||
private List<NewsEntry> parseNews(String newsContent) {
|
||||
List<NewsEntry> rv = new ArrayList<NewsEntry>();
|
||||
// Parse news content for headings.
|
||||
boolean foundEntry = false;
|
||||
int start = newsContent.indexOf("<h3>");
|
||||
while (start >= 0) {
|
||||
NewsEntry entry = new NewsEntry();
|
||||
// Add offset to start:
|
||||
// 4 - gets rid of <h3>
|
||||
// 16 - gets rid of the date as well (assuming form "<h3>yyyy-mm-dd: Foobarbaz...")
|
||||
// Don't truncate the "congratulations" in initial news
|
||||
if (newsContent.length() > start + 16 &&
|
||||
newsContent.substring(start + 4, start + 6).equals("20") &&
|
||||
newsContent.substring(start + 14, start + 16).equals(": ")) {
|
||||
entry.updated = RFC3339Date.parse3339Date(newsContent.substring(start + 4, start + 14));
|
||||
newsContent = newsContent.substring(start+16);
|
||||
} else {
|
||||
newsContent = newsContent.substring(start+4);
|
||||
}
|
||||
int end = newsContent.indexOf("</h3>");
|
||||
if (end >= 0) {
|
||||
String heading = newsContent.substring(0, end);
|
||||
entry.title = heading;
|
||||
// use title as UUID
|
||||
entry.id = heading;
|
||||
newsContent = newsContent.substring(end + 5);
|
||||
end = newsContent.indexOf("<h3>");
|
||||
if (end > 0)
|
||||
entry.content = newsContent.substring(0, end);
|
||||
else
|
||||
entry.content = newsContent;
|
||||
rv.add(entry);
|
||||
start = end;
|
||||
}
|
||||
}
|
||||
Collections.sort(rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (args.length != 0) {
|
||||
System.err.println("Usage: NewsManager");
|
||||
System.exit(1);
|
||||
}
|
||||
I2PAppContext ctx = new I2PAppContext();
|
||||
NewsManager mgr = new NewsManager(ctx, null, null);
|
||||
mgr.startup();
|
||||
List<NewsEntry> entries = mgr.getEntries();
|
||||
System.out.println("Loaded " + entries.size() + " news entries");
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
NewsEntry e = entries.get(i);
|
||||
System.out.println("\n****** News #" + (i+1) + ": " + e.title + ' ' + new Date(e.updated) + '\n' + e.content);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user