/*******************************************************************************
* Copyright (c) 2009, Adobe Systems Incorporated
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* · Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* · Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* · Neither the name of Adobe Systems Incorporated nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/
package com.adobe.dp.epub.opf;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Hashtable;
import java.util.Iterator;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import com.adobe.dp.epub.dtd.EPUBEntityResolver;
import com.adobe.dp.epub.io.ContainerSource;
import com.adobe.dp.epub.util.PathUtil;
import com.adobe.dp.epub.util.Translit;
import com.adobe.dp.xml.util.SMapImpl;
import com.adobe.dp.xml.util.XMLSerializer;
public class OPFResource extends Resource {
public static final String opfns = "http://www.idpf.org/2007/opf";
public static final String opfmedia = "application/oebps-package+xml";
public static final String dcns = "http://purl.org/dc/elements/1.1/";
private static final String dctermsns = "http://purl.org/dc/terms/";
private static final String xsins = "http://www.w3.org/2001/XMLSchema-instance";
class XMLHandler extends DefaultHandler {
String section;
String uniqueIdentifier;
String metaName;
String metaNS;
StringBuffer metaValue = new StringBuffer();
boolean primaryId;
Hashtable idmap = new Hashtable();
String base;
XMLHandler(String base) {
this.base = base;
}
public void characters(char[] ch, int start, int length) throws SAXException {
if (metaName != null)
metaValue.append(ch, start, length);
}
public void endElement(String uri, String localName, String name) throws SAXException {
if (uri.equals(opfns)) {
if (section != null && localName.equals(section))
section = null;
}
if (metaName != null) {
if (primaryId)
epub.addPrimaryIdentifier(metaValue.toString());
else
epub.addMetadata(metaNS, metaName, metaValue.toString());
metaName = null;
metaNS = null;
metaValue.delete(0, metaValue.length());
primaryId = false;
}
}
public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
if (uri.equals(opfns)) {
if (localName.equals("package")) {
uniqueIdentifier = attributes.getValue("unique-identifier");
return;
}
if (localName.equals("metadata") || localName.equals("manifest")) {
section = localName;
return;
}
if (localName.equals("spine")) {
section = "spine";
String toc = attributes.getValue("toc");
if (toc != null) {
Object res = idmap.get(toc);
if (res != null)
epub.toc = (Resource) res;
}
return;
}
if (section.equals("metadata")) {
if (localName.equals("meta")) {
String mname = attributes.getValue("name");
String mvalue = attributes.getValue("content");
epub.addMetadata(null, mname, mvalue);
}
} else if (section.equals("manifest")) {
if (localName.equals("item")) {
String href = attributes.getValue("href");
String type = attributes.getValue("media-type");
String id = attributes.getValue("id");
if (href != null) {
href = PathUtil.resolveRelativeReference(base, href);
try {
Resource res = epub.loadResource(href, type);
if (res != null && id != null)
idmap.put(id, res);
} catch (Exception e) {
e.printStackTrace();
}
}
}
} else if (section.equals("spine")) {
if (localName.equals("itemref")) {
String idref = attributes.getValue("idref");
if (idref != null) {
Object res = idmap.get(idref);
if (res != null)
epub.addToSpine((Resource) res);
}
}
}
} else if (section.equals("metadata")) {
if (metaName == null) {
metaNS = uri;
metaName = localName;
if (metaNS.equals(dcns) && metaName.equals("identifier")) {
String id = attributes.getValue("id");
primaryId = id != null && id.equals(uniqueIdentifier);
}
}
}
}
}
OPFResource(Publication epub, String name) {
super(epub, name, opfmedia, null);
}
public void load(ContainerSource container, String opfName) throws IOException {
InputStream in = container.getDataSource(opfName).getInputStream();
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
try {
SAXParser parser = factory.newSAXParser();
XMLReader reader = parser.getXMLReader();
XMLHandler handler = new XMLHandler(opfName);
reader.setContentHandler(handler);
reader.setEntityResolver(EPUBEntityResolver.instance);
InputSource source = new InputSource(in);
reader.parse(source);
} catch (ParserConfigurationException e) {
e.printStackTrace();
throw new RuntimeException(e.toString());
} catch (SAXException e) {
throw new IOException("XML Syntax error in OPF: " + e.getMessage());
} finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void serialize(OutputStream out) throws IOException {
XMLSerializer ser = new XMLSerializer(out);
ser.startDocument("1.0", "UTF-8");
SMapImpl nsmap = new SMapImpl();
nsmap.put(null, "opf", opfns);
nsmap.put(null, "dc", dcns);
nsmap.put(null, "dcterms", dctermsns);
nsmap.put(null, "xsi", xsins);
ser.setPreferredPrefixMap(nsmap);
SMapImpl attrs = new SMapImpl();
attrs.put(null, "version", "2.0");
attrs.put(null, "unique-identifier", "bookid");
ser.startElement(opfns, "package", attrs, true);
ser.newLine();
ser.startElement(opfns, "metadata", null, false);
ser.newLine();
Iterator it = epub.metadata.iterator();
int identifierCount = 0;
while (it.hasNext()) {
Publication.SimpleMetadata item = (Publication.SimpleMetadata) it.next();
if (item.ns != null && item.ns.equals(dcns) && item.name.equals("identifier")) {
//[LC] improved: added the possibility to control from the outside de UUID
attrs =(SMapImpl) item.attribs;
if(attrs==null){
attrs = new SMapImpl();
}
if(attrs.get(opfns, "scheme")!=null&&attrs.get(opfns, "scheme").equals("UUID")){
attrs.put(null, "id", (identifierCount == 0 ? "bookid" : "bookid" + identifierCount));
identifierCount++;
}
} else {
attrs = null;
}
String value = item.value;
if (epub.isTranslit())
value = Translit.translit(value);
if (item.ns == null) {
attrs = new SMapImpl();
attrs.put(null, "name", item.name);
attrs.put(null, "content", value);
ser.startElement(opfns, "meta", attrs, false);
ser.endElement(opfns, "meta");
ser.newLine();
} else {
ser.startElement(item.ns, item.name, attrs, false);
char[] arr = value.toCharArray();
ser.text(arr, 0, arr.length);
ser.endElement(item.ns, item.name);
ser.newLine();
}
}
ser.endElement(opfns, "metadata");
ser.newLine();
ser.startElement(opfns, "manifest", null, false);
ser.newLine();
it = epub.resources();
while (it.hasNext()) {
Resource r = (Resource) it.next();
if (r != this) {
attrs = new SMapImpl();
attrs.put(null, "id", epub.assignId(r));
attrs.put(null, "href", makeReference(r.getName(), null));
attrs.put(null, "media-type", r.mediaType);
ser.startElement(opfns, "item", attrs, false);
ser.endElement(opfns, "item");
ser.newLine();
}
}
ser.endElement(opfns, "manifest");
ser.newLine();
attrs = new SMapImpl();
if (epub.toc != null)
attrs.put(null, "toc", epub.assignId(epub.toc));
if (epub.pageMap != null)
attrs.put(null, "page-map", epub.assignId(epub.pageMap));
ser.startElement(opfns, "spine", attrs, false);
ser.newLine();
it = epub.spine();
while (it.hasNext()) {
Resource r = (Resource) it.next();
//[LC] improved: added the possibility to control from the outside de UUID
attrs=r.getSerializationAttributes();
if(attrs==null){
attrs = new SMapImpl();
}
attrs.put(null, "idref", epub.assignId(r));
ser.startElement(opfns, "itemref", attrs, false);
ser.endElement(opfns, "itemref");
ser.newLine();
}
ser.endElement(opfns, "spine");
ser.newLine();
ser.endElement(opfns, "package");
ser.newLine();
ser.endDocument();
}
}