package nota.oxygen.epub;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.beans.*;
import java.io.File;
import java.util.Map;
import nota.oxygen.common.Utils;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import de.schlichtherle.truezip.file.TArchiveDetector;
import de.schlichtherle.truezip.file.TConfig;
import de.schlichtherle.truezip.file.TFile;
import de.schlichtherle.truezip.fs.archive.zip.JarDriver;
import de.schlichtherle.truezip.socket.sl.IOPoolLocator;
@SuppressWarnings("serial")
public class Concatter extends JPanel implements ActionListener, PropertyChangeListener {
private static JFrame frame;
private JButton startButton;
private static JTextArea taskOutput;
private Task task;
private File[] listOfFiles;
private static PackageHandler packageHandler;
private static ConcatHandler concatHandler;
class Task extends SwingWorker<Void, Void> {
// Main task. Executed in background thread.
@Override
public Void doInBackground() {
if (!EpubUtils.start(taskOutput))
return null;
if (!EpubUtils.unzip(taskOutput))
return null;
if (!EpubUtils.canConcat(taskOutput))
return null;
if (!EpubUtils.backup(taskOutput))
return null;
EpubUtils.outputProcess("PREPARING AND PARSING", true, taskOutput);
// create package handler instance
packageHandler = new PackageHandler();
if (!EpubUtils.parseFile(new File(EpubUtils.EPUB_FOLDER + File.separator + EpubUtils.PACKAGE_FILENAME), packageHandler, taskOutput))
return null;
// get all xhtml files from extracted zip file
listOfFiles = EpubUtils.getFiles(false, true);
// create concat handler instance
concatHandler = new ConcatHandler();
for (File file : listOfFiles) {
// prepare source file
if (!EpubUtils.prepareFile(file, taskOutput))
return null;
// parse source file
if (!EpubUtils.parseFile(file, concatHandler, taskOutput))
return null;
}
EpubUtils.outputProcess("BUILDING CONCAT DOCUMENT", true, taskOutput);
// build concat document
Document concatDoc = buildConcatDocument();
if (concatDoc == null) {
return null;
}
// add unique ids to missing elements
EpubUtils.addUniqueIds(concatDoc.getDocumentElement(), taskOutput);
// clean references
if (!cleanReferences(concatDoc.getElementsByTagName("a"), EpubUtils.EPUB_FOLDER, taskOutput))
return null;
// save concat document
if (!EpubUtils.saveDocument(concatDoc, new File(EpubUtils.EPUB_FOLDER + File.separator + EpubUtils.CONCAT_FILENAME), taskOutput))
return null;
EpubUtils.outputProcess("MODIFYING PACKAGE DOCUMENT", true, taskOutput);
Document packageDoc = EpubUtils.createDocument(new File(EpubUtils.EPUB_FOLDER + File.separator + EpubUtils.PACKAGE_FILENAME), taskOutput);
if (packageDoc == null) {
return null;
}
// add concat document to opf document
if (!EpubUtils.addOpfItem(packageDoc, EpubUtils.CONCAT_FILENAME, 0, taskOutput))
return null;
// remove non concat documents from opf document
for (int i = 0; i < listOfFiles.length; i++) {
if (!EpubUtils.removeOpfItem(packageDoc, listOfFiles[i].getName(), taskOutput))
return null;
}
// remove fallback from non xhtml spine elements
if (!EpubUtils.removeFallbackFromOpf(packageDoc, taskOutput))
return null;
// save opf document
if (!EpubUtils.saveDocument(packageDoc, new File(EpubUtils.EPUB_FOLDER + File.separator + EpubUtils.PACKAGE_FILENAME), taskOutput))
return null;
EpubUtils.outputProcess("MODIFYING EPUB", true, taskOutput);
// obtain the global configuration
TConfig config = TConfig.get();
config.setArchiveDetector(new TArchiveDetector("epub", new JarDriver(IOPoolLocator.SINGLETON)));
// get epub file destination
String epubPath = EpubUtils.EPUB.getPath();
String epubFolder = EpubUtils.EPUB_FOLDER.substring(EpubUtils.EPUB_FOLDER.lastIndexOf(File.separator)).replace(File.separator, "");
TFile destination = new TFile(epubPath + File.separator + epubFolder);
// modify epub file destination
if (!EpubUtils.addFileToEpub(new TFile(EpubUtils.EPUB_FOLDER + File.separator + EpubUtils.CONCAT_FILENAME), destination, taskOutput))
return null;
if (!EpubUtils.addFileToEpub(new TFile(EpubUtils.EPUB_FOLDER + File.separator + EpubUtils.PACKAGE_FILENAME), destination, taskOutput))
return null;
for (int i = 0; i < listOfFiles.length; i++) {
if (!EpubUtils.removeFileFromEpub(new TFile(destination, listOfFiles[i].getName()), taskOutput))
return null;
}
// commit changes to epub file destination
if (!EpubUtils.commitChanges(taskOutput))
return null;
if (!EpubUtils.finish(taskOutput))
return null;
return null;
}
// Executed in event dispatching thread
@Override
public void done() {
Toolkit.getDefaultToolkit().beep();
startButton.setEnabled(true);
setCursor(null); // turn off the wait cursor
EpubUtils.outputMessage(taskOutput, "Done");
}
}
public Concatter() {
super(new BorderLayout());
// Create the demo's UI.
startButton = new JButton("Start");
startButton.setActionCommand("start");
startButton.addActionListener(this);
startButton.setVisible(false);
taskOutput = new JTextArea(30, 130);
taskOutput.setMargin(new Insets(5, 5, 5, 5));
taskOutput.setEditable(false);
JPanel panel = new JPanel();
panel.add(startButton);
add(panel, BorderLayout.PAGE_START);
add(new JScrollPane(taskOutput), BorderLayout.CENTER);
setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
startButton.doClick();
}
@Override
public void propertyChange(PropertyChangeEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void actionPerformed(ActionEvent evt) {
// Invoked when the user presses the start button.
startButton.setEnabled(false);
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
// Instances of javax.swing.SwingWorker are not reusuable, so we create new instances as needed.
this.task = new Task();
this.task.addPropertyChangeListener(this);
this.task.execute();
}
private static void createAndShowGUI() {
JComponent newContentPane = new Concatter();
newContentPane.setOpaque(true);
frame = new JFrame("Concatenating " + EpubUtils.EPUB.getName());
frame.setContentPane(newContentPane);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
EpubUtils.EPUB = new File(args[0]);
EpubUtils.EPUB_FOLDER = args[1];
EpubUtils.prepare("concatter", "concat");
// Schedule a job for the event-dispatching thread: creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
public static Document buildConcatDocument() {
try {
StringBuilder xmlTemplate = new StringBuilder();
xmlTemplate.append("<html");
for (Map.Entry<String, String> entry : concatHandler.getHtmlAttributes().entrySet()) {
xmlTemplate.append(" " + entry.getKey() + "='" + entry.getValue() + "'");
}
xmlTemplate.append(">");
xmlTemplate.append("<head>");
xmlTemplate.append("<meta charset='UTF-8'/>");
if (packageHandler.getTitle() != null) {
xmlTemplate.append("<title>" + packageHandler.getTitle() + "</title>");
} else {
xmlTemplate.append("<title>" + concatHandler.getTitle() + "</title>");
}
for (Map.Entry<String, String> entry : concatHandler.getMetaNodes().entrySet()) {
xmlTemplate.append("<meta content='" + entry.getValue() + "' name='" + entry.getKey() + "'/>");
}
for (String cssLink : concatHandler.getCssLinks()) {
xmlTemplate.append("<link href='" + cssLink + "' rel='stylesheet' type='text/css'/>");
}
xmlTemplate.append("</head>");
xmlTemplate.append("<body>");
for (String line : concatHandler.getBodyLines()) {
xmlTemplate.append(line);
}
xmlTemplate.append("</body>");
xmlTemplate.append("</html>");
//System.out.println(xmlTemplate.toString());
return Utils.deserializeDocument(xmlTemplate.toString(), null);
} catch (Exception e) {
EpubUtils.outputMessage(taskOutput, "Could not build concat document. An Exception occurred: " + e.getMessage());
}
return null;
}
public static boolean cleanReferences(NodeList refNodes, String epubFolder, JTextArea taskOutput) {
try {
EpubUtils.outputMessage(taskOutput, "Cleaning references");
for (int i = 0; i < refNodes.getLength(); i++) {
Node refNode = refNodes.item(i);
NamedNodeMap attrs = refNode.getAttributes();
for (int j = 0; j < attrs.getLength(); j++) {
Attr attr = (Attr) attrs.item(j);
if (attr.getNodeName().equalsIgnoreCase("href")) {
if (!attr.getNodeValue().contains("http:") && !attr.getNodeValue().contains("www.") && attr.getNodeValue().contains("#")) {
// remove file reference
attr.setNodeValue(attr.getNodeValue().substring(attr.getNodeValue().indexOf("#")));
} else if (!attr.getNodeValue().contains("http:") && !attr.getNodeValue().contains("www.") && !attr.getNodeValue().contains("#") && attr.getNodeValue().contains(".xhtml")) {
String fileRef = attr.getNodeValue();
// create default handler instance
FindIdHandler findIdHandler = new FindIdHandler();
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
saxParser.parse(new File(epubFolder + File.separator + fileRef), findIdHandler);
String bodyId = findIdHandler.getId();
if (bodyId == null || bodyId.equals("")) {
EpubUtils.outputMessage(taskOutput, "Reference " + attr.getNodeValue() + " could not be changed, no id found");
return false;
}
String tempNodeValue = attr.getNodeValue();
attr.setNodeValue("#" + bodyId);
EpubUtils.outputMessage(taskOutput, "Reference " + tempNodeValue + " changed to " + attr.getNodeValue());
}
}
}
}
return true;
} catch (Exception e) {
EpubUtils.outputMessage(taskOutput, "Could not clean references. An Exception occurred: " + e.getMessage());
}
return false;
}
}