package org.androiddaisyreader.model;
import static org.androiddaisyreader.model.XmlUtilities.mapUnsupportedEncoding;
import static org.androiddaisyreader.model.XmlUtilities.obtainEncodingStringFromInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
public class XmlSpecification extends DefaultHandler {
private XmlModel model;
private List<XmlModel> listModel = new ArrayList<XmlModel>();
private static final int NUM_LEVELS_AVAILABLE_IN_DAISY202 = 6;
private Element current;
private StringBuilder buffer = new StringBuilder();
public XmlSpecification() {
}
private enum Element {
H1, H2, H3, H4, H5, H6, SENT, LEVEL1, LEVEL2, LEVEL3, LEVEL4, LEVEL5, LEVEL6;
@Override
public String toString() {
return this.name().toLowerCase();
}
}
private static Map<String, Element> elementMap = new HashMap<String, Element>(
Element.values().length);
static {
for (Element e : Element.values()) {
elementMap.put(e.toString(), e);
}
}
private static Map<Element, Integer> levelMap = new HashMap<Element, Integer>(
NUM_LEVELS_AVAILABLE_IN_DAISY202);
static {
levelMap.put(Element.LEVEL1, 1);
levelMap.put(Element.LEVEL2, 2);
levelMap.put(Element.LEVEL3, 3);
levelMap.put(Element.LEVEL4, 4);
levelMap.put(Element.LEVEL5, 5);
levelMap.put(Element.LEVEL6, 6);
}
@Override
public void startElement(String uri, String localName, String name, Attributes attributes) {
current = elementMap.get(ParserUtilities.getName(localName, name));
if (current == null) {
return;
}
switch (current) {
case LEVEL1:
case LEVEL2:
case LEVEL3:
case LEVEL4:
case LEVEL5:
case LEVEL6:
handleStartOfHeading(current, attributes);
break;
case H1:
case H2:
case H3:
case H4:
case H5:
case H6:
buffer.setLength(0);
addSmilHref(attributes);
break;
case SENT:
handleStartOfSend(attributes);
break;
default:
// do nothing for now for unmatched elements
break;
}
}
private void handleStartOfHeading(Element heading, Attributes attributes) {
String id = getId(attributes);
int level = levelMap.get(heading);
model = new XmlModel();
model.setId(id);
model.setLevel(level);
}
private void addSmilHref(Attributes attributes) {
String smilHref = getSmilHref(attributes);
model.setSmilHref(smilHref);
}
private void handleStartOfSend(Attributes attributes) {
if (model != null && model.getSmilHref() == null) {
model.setSmilHref(getSmilHref(attributes));
model.setId(getId(attributes));
}
}
private String getId(Attributes attributes) {
return ParserUtilities.getValueForName("id", attributes);
}
private String getSmilHref(Attributes attributes) {
return ParserUtilities.getValueForName("smilref", attributes);
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
super.characters(ch, start, length);
buffer.append(ch, start, length);
}
@Override
public void endElement(String uri, String localName, String name) throws SAXException {
// add current element type to the book model.
current = elementMap.get(ParserUtilities.getName(localName, name));
if (current == null) {
return;
}
switch (current) {
case H1:
case H2:
case H3:
case H4:
case H5:
case H6:
addText();
break;
case LEVEL1:
case LEVEL2:
case LEVEL3:
case LEVEL4:
case LEVEL5:
case LEVEL6:
break;
default:
break;
}
}
private void addText() {
if (model.getText() == null) {
model.setText(buffer.toString());
}
listModel.add(model);
}
private List<XmlModel> build() {
return listModel;
}
public static List<XmlModel> readFromStream(InputStream contents) throws IOException {
String encoding = obtainEncodingStringFromInputStream(contents);
encoding = mapUnsupportedEncoding(encoding);
return readFromStream(contents, encoding);
}
public static List<XmlModel> readFromStream(InputStream contents, String encoding)
throws IOException {
XmlSpecification specification = new XmlSpecification();
try {
XMLReader saxParser = Smil.getSaxParser();
saxParser.setContentHandler(specification);
saxParser.parse(Smil.getInputSource(contents));
contents.close();
return specification.build();
} catch (Exception e) {
throw new IOException("Couldn't parse the xml file", e);
}
}
}