package com.jadn.cc.services;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import android.util.Log;
import com.jadn.cc.core.OrderingPreference;
import com.jadn.cc.core.Subscription;
import com.jadn.cc.core.Util;
import com.jadn.cc.trace.TraceUtil;
public class FileSubscriptionHelper implements SubscriptionHelper {
private static final String CONCAT_DIVIDER = "\\;";
private static final String REGEX_DIVIDER = "\\\\;";
private final File legacyFile;
private final File subscriptionFile;
public FileSubscriptionHelper(File subscriptionFile, File legacyFile) {
this.subscriptionFile = subscriptionFile;
this.legacyFile = legacyFile;
}
@Override
public boolean addSubscription(Subscription toAdd) {
List<Subscription> subs = getSubscriptions();
if (containsSubscriptionURL(subs, toAdd.url)) {
// we already have the URL stored:
return false;
} // endif
// test the url:
if (Util.isValidURL(toAdd.url)) {
// passed, put it in and save:
subs.add(toAdd);
saveSubscriptions(subs);
return true;
} else {
Log.e("CarCast", "addSubscription: bad url: " + toAdd.url);
return false;
}
}
/**
* Scan the list for a subscription by its URL
*
* @param subs the list to scan
* @param url the URL to look for
* @return <code>true</code> if found in the list, <code>false</code>
* otherwise.
*/
private boolean containsSubscriptionURL(List<Subscription> subs, String url) {
return indexOfSubscriptionURL(subs, url) != -1;
}
/**
* Insure that all properties are keyed properly, by URL. If a key is not a
* url, but the value is, then the pair is removed, then placed back in
* reverse.
*
* @param props the properties to scan
*/
List<Subscription> convertProperties(Properties props) {
List<Subscription> subscriptions = new ArrayList<Subscription>();
Set<Object> keys = props.keySet();
for (Object key : keys) {
String url = (String) key;
String nameAndMore = props.getProperty(url, "");
Subscription sub = convertProperty(url, nameAndMore);
if (sub != null) {
subscriptions.add(sub);
} // endif
} // endforeach
return subscriptions;
}
private Subscription convertProperty(String url, String nameAndMore) {
String[] split = nameAndMore.split(REGEX_DIVIDER);
if (split.length == 5) {
// best case, we should have all properties:
try {
String name = split[0];
int maxCount = Integer.valueOf(split[1]);
OrderingPreference pref = OrderingPreference.valueOf(split[2]);
boolean enabled = Boolean.valueOf(split[3]);
boolean priority = Boolean.valueOf(split[4]);
return new Subscription(name, url, maxCount, pref, enabled, priority);
} catch (Exception ex) {
Log.w("CarCast", "couldn't read subscription " + url + "=" + nameAndMore);
} // endtry
} else if (split.length == 4) {
// next best case, we should have everything except priority (default to false)
try {
String name = split[0];
int maxCount = Integer.valueOf(split[1]);
OrderingPreference pref = OrderingPreference.valueOf(split[2]);
boolean enabled = Boolean.valueOf(split[3]);
return new Subscription(name, url, maxCount, pref, enabled, false);
} catch (Exception ex) {
Log.w("CarCast", "couldn't read subscription " + url + "=" + nameAndMore);
} // endtry
} else if (split.length == 3) {
// third best case, we have all properties except enabled:
try {
String name = split[0];
int maxCount = Integer.valueOf(split[1]);
OrderingPreference pref = OrderingPreference.valueOf(split[2]);
return new Subscription(name, url, maxCount, pref);
} catch (Exception ex) {
Log.w("CarCast", "couldn't read subscription " + url + "=" + nameAndMore);
} // endtry
} else if (split.length == 1) {
String name = split[0];
// oops, missing extra properties:
return new Subscription(name, url);
} else {
Log.w("CarCast", "couldn't read subscription " + url + "=" + nameAndMore);
} // endif
return null;
}
@Override
public void deleteAllSubscriptions() {
List<Subscription> emptyList = Collections.emptyList();
saveSubscriptions(emptyList);
}
@Override
public boolean editSubscription(Subscription original, Subscription updated) {
List<Subscription> subs = getSubscriptions();
int idx = indexOfSubscriptionURL(subs, original.url);
if (idx != -1) {
subs.remove(idx);
subs.add(updated);
saveSubscriptions(subs);
return true;
} // endif
return false;
}
List<Subscription> getLegacySitesFromFile() {
if (!legacyFile.exists()) {
return Collections.emptyList();
}
try {
InputStream input = new FileInputStream(legacyFile);
return readLegacySites(input);
} catch (Exception e1) {
TraceUtil.report(e1);
return Collections.emptyList();
}
}
@Override
public List<Subscription> getSubscriptions() {
if (legacyFile.exists()) {
// we need to convert to the new format first:
List<Subscription> legacy = getLegacySitesFromFile();
saveSubscriptions(legacy);
legacyFile.delete();
// short-cut out:
return legacy;
}
if (!subscriptionFile.exists()) {
subscriptionFile.getParentFile().mkdirs();
return resetToDemoSubscriptions();
}
if (!subscriptionFile.exists()) {
return null;
}
try {
InputStream dis = new BufferedInputStream(new FileInputStream(subscriptionFile));
Properties props = new Properties();
props.load(dis);
return convertProperties(props);
} catch (Exception e1) {
TraceUtil.report(e1);
return Collections.emptyList();
}
}
/**
* Scan the list for a subscription by its URL
*
* @param subs the list to scan
* @param url the URL to look for
* @return the index in the list, or -1 if not found
*/
private int indexOfSubscriptionURL(List<Subscription> subs, String url) {
for (int i = 0; i < subs.size(); i++) {
Subscription sub = subs.get(i);
if (sub.url.equals(url)) {
return i;
} // endif
} // endfor
// not found:
return -1;
}
List<Subscription> readLegacySites(InputStream input) throws IOException {
List<Subscription> sites = new ArrayList<Subscription>();
DataInputStream dis = new DataInputStream(input);
String line = null;
while ((line = dis.readLine()) != null) {
int eq = line.indexOf('=');
if (eq != -1) {
String name = line.substring(0, eq);
String url = line.substring(eq + 1);
if (Util.isValidURL(url)) {
sites.add(new Subscription(name, url));
} else {
TraceUtil.report(new RuntimeException("invalid URL in line: '" + line + "'; URL was: " + url));
} // endif
} else {
TraceUtil.report(new RuntimeException("missing equals in line: " + line));
}
}
return sites;
}
@Override
public boolean removeSubscription(Subscription toRemove) {
List<Subscription> subs = getSubscriptions();
int idx = indexOfSubscriptionURL(subs, toRemove.url);
if (idx != -1) {
subs.remove(idx);
saveSubscriptions(subs);
return true;
} // endif
return false;
}
@Override
public boolean toggleSubscription(Subscription toToggle) {
List<Subscription> subs = getSubscriptions();
int idx = indexOfSubscriptionURL(subs, toToggle.url);
if (idx != -1) {
Subscription sub = subs.get(idx);
sub.enabled = !sub.enabled;
saveSubscriptions(subs);
return true;
} // endif
return false;
}
@Override
public List<Subscription> resetToDemoSubscriptions() {
List<Subscription> subs = new ArrayList<Subscription>();
subs.add(new Subscription("Quirks and Quarks", "http://www.cbc.ca/podcasting/includes/quirks.xml"));
subs.add(new Subscription("60 second science", "http://rss.sciam.com/sciam/60secsciencepodcast"));
subs.add(new Subscription("60 second psych", "http://rss.sciam.com/sciam/60-second-psych"));
subs.add(new Subscription("60 second earth", "http://rss.sciam.com/sciam/60-second-earth"));
saveSubscriptions(subs);
return subs;
}
private boolean saveSubscriptions(List<Subscription> subscriptions) {
try {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(subscriptionFile));
Properties outSubs = new Properties();
for (Subscription sub : subscriptions) {
String valueStr = sub.name + CONCAT_DIVIDER + sub.maxDownloads + CONCAT_DIVIDER + sub.orderingPreference.name() + CONCAT_DIVIDER + sub.enabled + CONCAT_DIVIDER + sub.priority;
outSubs.put(sub.url, valueStr);
} // endforeach
outSubs.store(bos, "Carcast Subscription File v3");
bos.close();
// success:
return true;
} catch (IOException e) {
TraceUtil.report(e);
// failure:
return false;
}
}
}