package be.nikiroo.fanfix.supported; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.jsoup.helper.DataUtil; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.nodes.Node; import be.nikiroo.fanfix.Instance; import be.nikiroo.fanfix.bundles.StringId; import be.nikiroo.fanfix.data.Chapter; import be.nikiroo.fanfix.data.MetaData; import be.nikiroo.fanfix.data.Story; import be.nikiroo.utils.Progress; import be.nikiroo.utils.StringUtils; /** * This class is the base class used by the other support classes. It can be * used outside of this package, and have static method that you can use to get * access to the correct support class. *
* It will be used with 'resources' (usually web pages or files). * * @author niki */ public abstract class BasicSupport { private Document sourceNode; private URL source; private SupportType type; private URL currentReferer; // with only one 'r', as in 'HTTP'... /** * The name of this support class. * * @return the name */ protected abstract String getSourceName(); /** * Check if the given resource is supported by this {@link BasicSupport}. * * @param url * the resource to check for * * @return TRUE if it is */ protected abstract boolean supports(URL url); /** * Return TRUE if the support will return HTML encoded content values for * the chapters content. * * @return TRUE for HTML */ protected abstract boolean isHtml(); /** * Return the {@link MetaData} of this story. * * @return the associated {@link MetaData}, never NULL * * @throws IOException * in case of I/O error */ protected abstract MetaData getMeta() throws IOException; /** * Return the story description. * * @return the description * * @throws IOException * in case of I/O error */ protected abstract String getDesc() throws IOException; /** * Return the list of chapters (name and resource). *
* Can be NULL if this {@link BasicSupport} do no use chapters.
*
* @param pg
* the optional progress reporter
*
* @return the chapters or NULL
*
* @throws IOException
* in case of I/O error
*/
protected abstract List
* You are expected to call the super method implementation if you override
* it.
*
* @return the cookies
*/
public Map
* Can return NULL, in which case you are supposed to work without a source
* node.
*
* @param source
* the source {@link URL}
*
* @return the {@link InputStream}
*
* @throws IOException
* in case of I/O error
*/
protected Document loadDocument(URL source) throws IOException {
String url = getCanonicalUrl(source).toString();
return DataUtil.load(Instance.getCache().open(source, this, false),
"UTF-8", url.toString());
}
/**
* Log into the support (can be a no-op depending upon the support).
*
* @throws IOException
* in case of I/O error
*/
protected void login() throws IOException {
}
/**
* Now that we have processed the {@link Story}, close the resources if any.
*/
protected void close() {
setCurrentReferer(null);
}
/**
* Process the given story resource into a partially filled {@link Story}
* object containing the name and metadata.
*
* @param getDesc
* retrieve the description of the story, or not
* @param pg
* the optional progress reporter
*
* @return the {@link Story}, never NULL
*
* @throws IOException
* in case of I/O error
*/
protected Story processMeta(boolean getDesc, Progress pg)
throws IOException {
if (pg == null) {
pg = new Progress();
} else {
pg.setMinMax(0, 100);
}
pg.setProgress(30);
Story story = new Story();
MetaData meta = getMeta();
if (meta.getCreationDate() == null || meta.getCreationDate().isEmpty()) {
meta.setCreationDate(StringUtils.fromTime(new Date().getTime()));
}
story.setMeta(meta);
pg.setProgress(50);
if (meta.getCover() == null) {
meta.setCover(BasicSupportHelper.getDefaultCover(meta.getSubject()));
}
pg.setProgress(60);
if (getDesc) {
String descChapterName = Instance.getTrans().getString(
StringId.DESCRIPTION);
story.getMeta().setResume(
BasicSupportPara.makeChapter(this, source, 0,
descChapterName, //
getDesc(), isHtml(), null));
}
pg.done();
return story;
}
/**
* Process the given story resource into a fully filled {@link Story}
* object.
*
* @param pg
* the optional progress reporter
*
* @return the {@link Story}, never NULL
*
* @throws IOException
* in case of I/O error
*/
// ADD final when BasicSupport_Deprecated is gone
public Story process(Progress pg) throws IOException {
setCurrentReferer(source);
login();
sourceNode = loadDocument(source);
try {
return doProcess(pg);
} finally {
close();
}
}
/**
* Actual processing step, without the calls to other methods.
*
* Will convert the story resource into a fully filled {@link Story} object.
*
* @param pg
* the optional progress reporter
*
* @return the {@link Story}, never NULL
*
* @throws IOException
* in case of I/O error
*/
protected Story doProcess(Progress pg) throws IOException {
if (pg == null) {
pg = new Progress();
} else {
pg.setMinMax(0, 100);
}
pg.setProgress(1);
Progress pgMeta = new Progress();
pg.addProgress(pgMeta, 10);
Story story = processMeta(true, pgMeta);
pgMeta.done(); // 10%
pg.setName("Retrieving " + story.getMeta().getTitle());
Progress pgGetChapters = new Progress();
pg.addProgress(pgGetChapters, 10);
story.setChapters(new ArrayList