Index: tika-parsers/src/main/resources/META-INF/services/org.apache.tika.parser.Parser
===================================================================
--- tika-parsers/src/main/resources/META-INF/services/org.apache.tika.parser.Parser (revision 933894)
+++ tika-parsers/src/main/resources/META-INF/services/org.apache.tika.parser.Parser (revision )
@@ -32,3 +32,4 @@
org.apache.tika.parser.txt.TXTParser
org.apache.tika.parser.video.FLVParser
org.apache.tika.parser.xml.DcXMLParser
+org.apache.tika.parser.iwork.IWorkParser
Index: tika-parsers/src/test/java/org/apache/tika/parser/iwork/IWorkParserTest.java
===================================================================
--- tika-parsers/src/test/java/org/apache/tika/parser/iwork/IWorkParserTest.java (revision )
+++ tika-parsers/src/test/java/org/apache/tika/parser/iwork/IWorkParserTest.java (revision )
@@ -0,0 +1,69 @@
+package org.apache.tika.parser.iwork;
+
+import junit.framework.TestCase;
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.parser.ParseContext;
+import org.apache.tika.sax.BodyContentHandler;
+import org.xml.sax.ContentHandler;
+
+import java.io.InputStream;
+
+/**
+ *
+ */
+public class IWorkParserTest extends TestCase {
+
+ private IWorkParser iWorkParser;
+
+ @Override
+ protected void setUp() throws Exception {
+ iWorkParser = new IWorkParser();
+ }
+
+ public void testParseKeynote() throws Exception {
+ InputStream input = IWorkParserTest.class.getResourceAsStream("/test-documents/testKeynote.key");
+ Metadata metadata = new Metadata();
+ ContentHandler handler = new BodyContentHandler();
+ ParseContext parseContext = new ParseContext();
+
+ iWorkParser.parse(input, handler, metadata, parseContext);
+
+ assertEquals(6, metadata.size());
+ assertEquals("application/vnd.apple.keynote", metadata.get(Metadata.CONTENT_TYPE));
+ assertEquals("2", metadata.get(Metadata.SLIDE_COUNT));
+ assertEquals("1024", metadata.get(KeynoteExtractor.PRESENTATION_WIDTH));
+ assertEquals("768", metadata.get(KeynoteExtractor.PRESENTATION_HEIGHT));
+ assertEquals("Tika user", metadata.get(Metadata.AUTHOR));
+ assertEquals("Apache tika", metadata.get(Metadata.TITLE));
+
+ String content = handler.toString();
+ assertTrue(content.contains("A sample presentation"));
+ assertTrue(content.contains("For the Apache Tika project"));
+ assertTrue(content.contains("Slide 1"));
+ assertTrue(content.contains("Some random text for the sake of testability."));
+ assertTrue(content.contains("A nice comment"));
+ assertTrue(content.contains("A nice note"));
+ }
+
+ public void testParsePages() throws Exception {
+ InputStream input = IWorkParserTest.class.getResourceAsStream("/test-documents/testPages.pages");
+ Metadata metadata = new Metadata();
+ ContentHandler handler = new BodyContentHandler();
+ ParseContext parseContext = new ParseContext();
+
+ iWorkParser.parse(input, handler, metadata, parseContext);
+
+ assertEquals(51, metadata.size());
+ assertEquals("application/vnd.apple.pages", metadata.get(Metadata.CONTENT_TYPE));
+ assertEquals("Tika user", metadata.get(Metadata.AUTHOR));
+ assertEquals("Apache tika", metadata.get(Metadata.TITLE));
+ assertEquals("2010-05-09T21:34:38+0200", metadata.get(Metadata.CREATION_DATE));
+ assertEquals("2010-05-09T23:50:36+0200", metadata.get(Metadata.LAST_MODIFIED));
+ assertEquals("en", metadata.get(Metadata.LANGUAGE));
+ assertEquals("2", metadata.get(Metadata.PAGE_COUNT));
+
+ String content = handler.toString();
+ System.out.println(content);
+ }
+
+}
Index: tika-parsers/src/main/java/org/apache/tika/parser/iwork/NumbersExtractor.java
===================================================================
--- tika-parsers/src/main/java/org/apache/tika/parser/iwork/NumbersExtractor.java (revision )
+++ tika-parsers/src/main/java/org/apache/tika/parser/iwork/NumbersExtractor.java (revision )
@@ -0,0 +1,7 @@
+package org.apache.tika.parser.iwork;
+
+/**
+ *
+ */
+public class NumbersExtractor {
+}
Index: tika-parsers/src/main/java/org/apache/tika/parser/iwork/IWorkParser.java
===================================================================
--- tika-parsers/src/main/java/org/apache/tika/parser/iwork/IWorkParser.java (revision )
+++ tika-parsers/src/main/java/org/apache/tika/parser/iwork/IWorkParser.java (revision )
@@ -0,0 +1,71 @@
+package org.apache.tika.parser.iwork;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.mime.MediaType;
+import org.apache.tika.parser.ParseContext;
+import org.apache.tika.parser.Parser;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+/**
+ * A parser for the IWork formats.
+ *
+ * Currently supported formats:
+ *
+ * - Keynote format version 2.x. Currently only tested with Keynote version 5.x
+ *
- Pages format version 1.x. Currently only tested with Keynote version 4.0.x
+ *
+ */
+public class IWorkParser implements Parser {
+
+ private final static Set supportedTypes =
+ Collections.unmodifiableSet(
+ new HashSet(Arrays.asList(
+ MediaType.application("vnd.apple.keynote"),
+ MediaType.application("vnd.apple.pages"),
+ MediaType.application("vnd.apple.numbers")
+ )));
+
+ public Set getSupportedTypes(ParseContext context) {
+ return supportedTypes;
+ }
+
+ public void parse(InputStream stream, ContentHandler handler, Metadata metadata, ParseContext context) throws IOException, SAXException, TikaException {
+ ZipInputStream zipInputStream = new ZipInputStream(stream);
+
+ ZipEntry entry;
+ while ((entry = zipInputStream.getNextEntry()) != null) {
+ if ("index.apxl".equals(entry.getName())) {
+ if (metadata.get(Metadata.CONTENT_TYPE) == null) {
+ metadata.set(Metadata.CONTENT_TYPE, "application/vnd.apple.keynote");
+ }
+ new KeynoteExtractor().parse(zipInputStream, handler, metadata, context);
+ break;
+ } else if ("index.xml".equals(entry.getName())) {
+ if (metadata.get(Metadata.CONTENT_TYPE) == null) {
+ metadata.set(Metadata.CONTENT_TYPE, "application/vnd.apple.pages");
+ }
+ new PagesExtractor().parse(zipInputStream, handler, metadata, context);
+ break;
+ } else if ("index.xml".equals(entry.getName())) {
+ // Numbers has index.xml as well. Therefore the filename cannot be used for detecting type.
+ // The xml file should be sniffed before determining the extractor
+ throw new UnsupportedOperationException("Cannot parse a Numbers document yet");
+ }
+ }
+ }
+
+ public void parse(InputStream stream, ContentHandler handler, Metadata metadata) throws IOException, SAXException, TikaException {
+ parse(stream, handler, metadata, new ParseContext());
+ }
+}
Index: tika-parsers/src/main/java/org/apache/tika/parser/iwork/AbstractIWorkExtractor.java
===================================================================
--- tika-parsers/src/main/java/org/apache/tika/parser/iwork/AbstractIWorkExtractor.java (revision )
+++ tika-parsers/src/main/java/org/apache/tika/parser/iwork/AbstractIWorkExtractor.java (revision )
@@ -0,0 +1,28 @@
+package org.apache.tika.parser.iwork;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.io.CloseShieldInputStream;
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.parser.ParseContext;
+import org.apache.tika.parser.xml.XMLParser;
+import org.apache.tika.sax.OfflineContentHandler;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ *
+ */
+public abstract class AbstractIWorkExtractor extends XMLParser {
+
+ @Override
+ public void parse(InputStream stream, ContentHandler handler, Metadata metadata, ParseContext context) throws IOException, SAXException, TikaException {
+ getSAXParser(context).parse(
+ new CloseShieldInputStream(stream),
+ new OfflineContentHandler(
+ getContentHandler(handler, metadata)));
+ }
+
+}
Index: tika-parsers/src/test/java/org/apache/tika/parser/AutoDetectParserTest.java
===================================================================
--- tika-parsers/src/test/java/org/apache/tika/parser/AutoDetectParserTest.java (revision 925321)
+++ tika-parsers/src/test/java/org/apache/tika/parser/AutoDetectParserTest.java (revision )
@@ -16,15 +16,14 @@
*/
package org.apache.tika.parser;
-import java.io.IOException;
-import java.io.InputStream;
-
+import junit.framework.TestCase;
import org.apache.tika.exception.TikaException;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.sax.BodyContentHandler;
import org.xml.sax.ContentHandler;
-import junit.framework.TestCase;
+import java.io.IOException;
+import java.io.InputStream;
public class AutoDetectParserTest extends TestCase {
@@ -34,6 +33,7 @@
private static final String HTML = "text/html";
private static final String PDF = "application/pdf";
private static final String POWERPOINT = "application/vnd.ms-powerpoint";
+ private static final String KEYNOTE = "application/vnd.apple.keynote";
private static final String RTF = "application/rtf";
private static final String PLAINTEXT = "text/plain";
private static final String WORD = "application/msword";
@@ -123,6 +123,10 @@
assertAutoDetect(resource, badResource, type, wrongMimeType, content);
}
+ public void testKeynote() throws Exception {
+ assertAutoDetect("testKeynote.key", KEYNOTE, "A sample presentation");
+ }
+
public void testEpub() throws Exception {
assertAutoDetect(
"testEPUB.epub", "application/epub+zip",
Index: tika-parsers/src/main/java/org/apache/tika/parser/iwork/KeynoteExtractor.java
===================================================================
--- tika-parsers/src/main/java/org/apache/tika/parser/iwork/KeynoteExtractor.java (revision )
+++ tika-parsers/src/main/java/org/apache/tika/parser/iwork/KeynoteExtractor.java (revision )
@@ -0,0 +1,139 @@
+package org.apache.tika.parser.iwork;
+
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.sax.XHTMLContentHandler;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ *
+ */
+public class KeynoteExtractor extends AbstractIWorkExtractor {
+
+ public final static String PRESENTATION_WIDTH = "slides-width";
+ public final static String PRESENTATION_HEIGHT = "slides-height";
+
+ @Override
+ protected ContentHandler getContentHandler(ContentHandler handler, Metadata metadata) {
+ return new KeynoteContentHandler(handler, metadata);
+ }
+
+ private static class KeynoteContentHandler extends DefaultHandler {
+
+ private final XHTMLContentHandler xhtml;
+ private final Metadata metadata;
+
+ private boolean inSlide = false;
+ private boolean inTheme = false;
+ private boolean inTitle = false;
+ private boolean inBody = false;
+
+ private boolean inMetadata = false;
+ private boolean inMetaDataTitle = false;
+ private boolean inMetaDataAuthors = false;
+
+ private boolean stickNote = false;
+ private boolean notes = false;
+
+ private boolean inParsableText = false;
+
+ private int numberOfSlides = 0;
+
+ private KeynoteContentHandler(ContentHandler handler, Metadata metadata) {
+ this.metadata = metadata;
+ xhtml = new XHTMLContentHandler(handler, metadata);
+ }
+
+ @Override
+ public void startDocument() throws SAXException {
+ xhtml.startDocument();
+ }
+
+ @Override
+ public void endDocument() throws SAXException {
+ xhtml.endDocument();
+ metadata.set(Metadata.SLIDE_COUNT, String.valueOf(numberOfSlides));
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+ if ("key:theme".equals(qName)) {
+ inTheme = true;
+ } else if ("key:slide".equals(qName)) {
+ inSlide = true;
+ numberOfSlides++;
+ xhtml.startElement("div");
+ } else if ("key:title-placeholder".equals(qName) && inSlide) {
+ inTitle = true;
+ xhtml.startElement("h1");
+ } else if ("sf:sticky-note".equals(qName) && inSlide) {
+ xhtml.startElement("p");
+ } else if ("key:notes".equals(qName) && inSlide) {
+ xhtml.startElement("p");
+ } else if ("key:body-placeholder".equals(qName) && inSlide) {
+ xhtml.startElement("p");
+ inBody = true;
+ } else if ("key:size".equals(qName) && !inTheme) {
+ String width = attributes.getValue("sfa:w");
+ String height = attributes.getValue("sfa:h");
+ metadata.set(PRESENTATION_WIDTH, width);
+ metadata.set(PRESENTATION_HEIGHT, height);
+ } else if ("sf:text-body".equals(qName)) {
+ inParsableText = true;
+ } else if ("key:metadata".equals(qName)) {
+ inMetadata = true;
+ } else if (inMetadata && "key:title".equals(qName)) {
+ inMetaDataTitle = true;
+ } else if (inMetadata && "key:authors".equals(qName)) {
+ inMetaDataAuthors = true;
+ } else if (inMetaDataTitle && "key:string".equals(qName)) {
+ metadata.set(Metadata.TITLE, attributes.getValue("sfa:string"));
+ } else if (inMetaDataAuthors && "key:string".equals(qName)) {
+ metadata.add(Metadata.AUTHOR, attributes.getValue("sfa:string"));
+ }
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ if ("key:theme".equals(qName)) {
+ inTheme = false;
+ } else if ("key:slide".equals(qName)) {
+ inSlide = false;
+ xhtml.endElement("div");
+ } else if ("key:title-placeholder".equals(qName) && inSlide) {
+ inTitle = false;
+ xhtml.endElement("h1");
+ } else if ("sf:sticky-note".equals(qName) && inSlide) {
+ xhtml.endElement("p");
+ } else if ("key:notes".equals(qName) && inSlide) {
+ xhtml.endElement("p");
+ } else if ("key:body-placeholder".equals(qName) && inSlide) {
+ xhtml.endElement("p");
+ inBody = false;
+ } else if ("sf:text-body".equals(qName)) {
+ inParsableText = false;
+ } else if ("key:metadata".equals(qName)) {
+ inMetadata = false;
+ } else if (inMetadata && "key:title".equals(qName)) {
+ inMetaDataTitle = false;
+ } else if (inMetadata && "key:authors".equals(qName)) {
+ inMetaDataAuthors = false;
+ }
+ }
+
+ @Override
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ if (!inParsableText || !inSlide) {
+ return;
+ }
+
+ String text = new String(ch, start, length).trim();
+ if (text.length() != 0) {
+ xhtml.characters(text);
+ }
+ }
+ }
+
+}
Index: tika-parsers/src/main/java/org/apache/tika/parser/xml/XMLParser.java
===================================================================
--- tika-parsers/src/main/java/org/apache/tika/parser/xml/XMLParser.java (revision 911195)
+++ tika-parsers/src/main/java/org/apache/tika/parser/xml/XMLParser.java (revision )
@@ -16,18 +16,6 @@
*/
package org.apache.tika.parser.xml;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-import javax.xml.XMLConstants;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-
import org.apache.tika.exception.TikaException;
import org.apache.tika.io.CloseShieldInputStream;
import org.apache.tika.metadata.Metadata;
@@ -42,6 +30,17 @@
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
+import javax.xml.XMLConstants;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
/**
* XML parser.
*
@@ -116,7 +115,7 @@
* @return SAX parser
* @throws TikaException if a SAX parser could not be created
*/
- private SAXParser getSAXParser(ParseContext context)
+ protected SAXParser getSAXParser(ParseContext context)
throws TikaException {
SAXParser parser = context.get(SAXParser.class);
if (parser instanceof SAXParser) {
Index: tika-core/src/main/resources/org/apache/tika/mime/tika-mimetypes.xml
===================================================================
--- tika-core/src/main/resources/org/apache/tika/mime/tika-mimetypes.xml (revision 911452)
+++ tika-core/src/main/resources/org/apache/tika/mime/tika-mimetypes.xml (revision )
@@ -556,6 +556,17 @@
+
+
+
+
+
+
+
+
+
+
+
Index: tika-parsers/src/main/java/org/apache/tika/parser/iwork/PagesExtractor.java
===================================================================
--- tika-parsers/src/main/java/org/apache/tika/parser/iwork/PagesExtractor.java (revision )
+++ tika-parsers/src/main/java/org/apache/tika/parser/iwork/PagesExtractor.java (revision )
@@ -0,0 +1,214 @@
+package org.apache.tika.parser.iwork;
+
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.sax.XHTMLContentHandler;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ *
+ */
+public class PagesExtractor extends AbstractIWorkExtractor {
+
+ @Override
+ protected ContentHandler getContentHandler(ContentHandler handler, Metadata metadata) {
+ return new PagesContentHandler(handler, metadata);
+ }
+
+ private final static class PagesContentHandler extends DefaultHandler {
+
+ private final XHTMLContentHandler xhtml;
+ private final Metadata metadata;
+
+ private boolean inMetaDataPart = false;
+ private boolean parseProperty = false;
+ private boolean inParsableText = false;
+ private int pageCount = 0;
+
+ private Map>> tableData = new HashMap>>();
+ private String activeTableId;
+ private int numberOfColumns = 0;
+ private List activeRow = new ArrayList();
+
+ private String metaDataLocalName;
+ private String metaDataQName;
+
+ private PagesContentHandler(ContentHandler contentHandler, Metadata metadata) {
+ xhtml = new XHTMLContentHandler(contentHandler, metadata);
+ this.metadata = metadata;
+ }
+
+ @Override
+ public void startDocument() throws SAXException {
+ xhtml.startDocument();
+ }
+
+ @Override
+ public void endDocument() throws SAXException {
+ metadata.set(Metadata.PAGE_COUNT, String.valueOf(pageCount));
+ if (pageCount > 0) {
+ xhtml.endElement("div");
+ }
+ xhtml.endDocument();
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+ if (parseProperty) {
+ String value = parsePrimitiveElementValue(qName, attributes);
+ if (value != null) {
+ String metaDataKey = resolveMetaDataKey(metaDataLocalName);
+ metadata.add(metaDataKey, value);
+ }
+ }
+
+ if ("sl:publication-info".equals(qName)) {
+ inMetaDataPart = true;
+ } else if ("sf:metadata".equals(qName)) {
+ inMetaDataPart = true;
+ } else if ("sf:page-start".equals(qName)) {
+ if (pageCount > 0) {
+ xhtml.endElement("div");
+ }
+ xhtml.startElement("div");
+ pageCount++;
+ } else if ("sf:p".equals(qName) && pageCount > 0) {
+ inParsableText = true;
+ xhtml.startElement("p");
+ } else if ("sf:attachment".equals(qName)) {
+ String kind = attributes.getValue("sf:kind");
+ if ("tabular-attachment".equals(kind)) {
+ activeTableId = attributes.getValue("sfa:ID");
+ tableData.put(activeTableId, new ArrayList>());
+ }
+ } else if ("sf:attachment-ref".equals(qName)) {
+ String idRef = attributes.getValue("sfa:IDREF");
+ outputTable(idRef);
+ }
+
+ if (activeTableId != null) {
+ parseTableData(qName, attributes);
+ }
+
+ if (inMetaDataPart) {
+ metaDataLocalName = localName;
+ metaDataQName = qName;
+ parseProperty = true;
+ }
+ }
+
+ @Override
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ if (metaDataLocalName != null && metaDataLocalName.equals(localName)) {
+ metaDataLocalName = null;
+ parseProperty = false;
+ }
+
+ if ("sl:publication-info".equals(qName)) {
+ inMetaDataPart = false;
+ } else if ("sf:metadata".equals(qName)) {
+ inMetaDataPart = false;
+ } else if ("sf:p".equals(qName) && pageCount > 0) {
+ inParsableText = false;
+ xhtml.endElement("p");
+ } else if ("sf:attachment".equals(qName)) {
+ activeTableId = null;
+ }
+ }
+
+ @Override
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ if (!inParsableText) {
+ return;
+ }
+
+ String text = new String(ch, start, length).trim();
+ if (text.length() != 0) {
+ xhtml.characters(text);
+ }
+ }
+
+ private void parseTableData(String qName, Attributes attributes) {
+ if ("sf:grid".equals(qName)) {
+ String numberOfColumns = attributes.getValue("sf:numcols");
+ this.numberOfColumns = Integer.parseInt(numberOfColumns);
+ } else if ("sf:ct".equals(qName)) {
+ activeRow.add(attributes.getValue("sfa:s"));
+
+ if (activeRow.size() >= 3) {
+ tableData.get(activeTableId).add(activeRow);
+ activeRow = new ArrayList();
+ }
+ }
+ }
+
+ private void outputTable(String idRef) throws SAXException {
+ List> tableData = this.tableData.get(idRef);
+ if (tableData != null) {
+ xhtml.startElement("table");
+ for (List row : tableData) {
+ xhtml.startElement("tr");
+ for (String cell : row) {
+ xhtml.element("td", cell);
+ }
+ xhtml.endElement("tr");
+ }
+ xhtml.endElement("table");
+ }
+ }
+
+ /**
+ * Returns a resolved key that is common in other document types or returns the specified metaDataLocalName if no
+ * common key could be found.
+ *
+ * @param metaDataLocalName The localname of the element containing metadata
+ * @return a resolved key that is common in other document types
+ */
+ private String resolveMetaDataKey(String metaDataLocalName) {
+ String metaDataKey = metaDataLocalName;
+ if ("sf:authors".equals(metaDataQName)) {
+ metaDataKey = Metadata.AUTHOR;
+ } else if ("sf:title".equals(metaDataQName)) {
+ metaDataKey = Metadata.TITLE;
+ } else if ("sl:SLCreationDateProperty".equals(metaDataQName)) {
+ metaDataKey = Metadata.CREATION_DATE;
+ } else if ("sl:SLLastModifiedDateProperty".equals(metaDataQName)) {
+ metaDataKey = Metadata.LAST_MODIFIED;
+ } else if ("sl:language".equals(metaDataQName)) {
+ metaDataKey = Metadata.LANGUAGE;
+ }
+ return metaDataKey;
+ }
+
+ /**
+ * Returns the value of a primitive element e.g.:
+ * - the number attribute
+ * = the string attribute
+ *
+ * Returns null
if the value could not be extracted from the list of attributes.
+ *
+ * @param qName The fully qualified name of the element containing the value to extract
+ * @param attributes The list of attributes of which one contains the value to be extracted
+ * @return the value of a primitive element
+ */
+ private String parsePrimitiveElementValue(String qName, Attributes attributes) {
+ if ("sl:string".equals(qName) || "sf:string".equals(qName)) {
+ return attributes.getValue("sfa:string");
+ } else if ("sl:number".equals(qName)) {
+ return attributes.getValue("sfa:number");
+ } else if ("sl:date".equals(qName)) {
+ return attributes.getValue("sf:val");
+ }
+
+ return null;
+ }
+ }
+
+}