PageRenderTime 46ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/androjena/src/it/polimi/dei/dbgroup/pedigree/androjena/xml/ExpatReaderWrapper.java

http://androjena.googlecode.com/
Java | 424 lines | 331 code | 53 blank | 40 comment | 46 complexity | d52b5d8ad07c9c742b7ff9263e1ecc9d MD5 | raw file
Possible License(s): Apache-2.0, GPL-3.0
  1. /*
  2. * Copyright 2010 Lorenzo Carrara
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package it.polimi.dei.dbgroup.pedigree.androjena.xml;
  17. import java.io.IOException;
  18. import java.util.HashMap;
  19. import java.util.HashSet;
  20. import java.util.Map;
  21. import java.util.Set;
  22. import java.util.Stack;
  23. import org.apache.xerces.util.XMLChar;
  24. import org.xml.sax.Attributes;
  25. import org.xml.sax.ContentHandler;
  26. import org.xml.sax.DTDHandler;
  27. import org.xml.sax.EntityResolver;
  28. import org.xml.sax.ErrorHandler;
  29. import org.xml.sax.InputSource;
  30. import org.xml.sax.Locator;
  31. import org.xml.sax.SAXException;
  32. import org.xml.sax.SAXNotRecognizedException;
  33. import org.xml.sax.SAXNotSupportedException;
  34. import org.xml.sax.XMLReader;
  35. import org.xml.sax.helpers.AttributesImpl;
  36. import org.xml.sax.helpers.XMLReaderFactory;
  37. /**
  38. * This class wraps an Expat XMLReader (Android's default SAX reader) to provide
  39. * compatibility with Xerces XMLReader behavior. For example, the method
  40. * startElement() will provide the qName argument, which is left null by Expat
  41. * (which is allowed, according to SAX specifications). Jena relies heavily on
  42. * Xerces behavior, so it has to be "simulated" in Android for Jena to work
  43. * properly.
  44. *
  45. * @author lorenzo carrara
  46. *
  47. */
  48. public class ExpatReaderWrapper implements XMLReader {
  49. private static final String FEATURE_NAMESPACES = "http://xml.org/sax/features/namespaces";
  50. private static final String FEATURE_NAMESPACE_PREFIXES = "http://xml.org/sax/features/namespace-prefixes";
  51. private static final String NAMESPACE_DECLARATION_PREFIX = "xmlns";
  52. private static final String NAMESPACE_DECLARATION_URI = "http://www.w3.org/2000/xmlns/";
  53. private static final String XML_PREFIX = "xml";
  54. private static final String XML_URI = "http://www.w3.org/XML/1998/namespace";
  55. private boolean namespaces = true;
  56. private boolean namespacePrefixes = false;
  57. private XMLReader expatReader = null;
  58. private ContentHandler contentHandler = null;
  59. private Locator documentLocator = null;
  60. private final Map<String, Mapping> prefixMappings = new HashMap<String, Mapping>();
  61. private final Stack<Set<String>> mappedPrefixes = new Stack<Set<String>>();
  62. private final ContentHandler handler = new ContentHandler() {
  63. @Override
  64. public void characters(char[] ch, int start, int length)
  65. throws SAXException {
  66. contentHandler.characters(ch, start, length);
  67. }
  68. @Override
  69. public void endDocument() throws SAXException {
  70. contentHandler.endDocument();
  71. }
  72. @Override
  73. public void endElement(String uri, String localName, String qName)
  74. throws SAXException {
  75. ExpandedName splitName = parseQName(qName);
  76. uri = splitName.uri;
  77. localName = splitName.localName;
  78. contentHandler.endElement(uri, localName, qName);
  79. // remove prefix mappings
  80. if (mappedPrefixes.isEmpty())
  81. throwException("unmatched element close tag " + qName);
  82. Set<String> mapped = mappedPrefixes.pop();
  83. for (String prefix : mapped) {
  84. Mapping mapping = prefixMappings.get(prefix);
  85. if (mapping == null)
  86. throwException("internal error");
  87. if (mapping.next == null)
  88. prefixMappings.remove(prefix);
  89. else
  90. prefixMappings.put(prefix, mapping.next);
  91. contentHandler.endPrefixMapping(prefix);
  92. }
  93. }
  94. @Override
  95. public void endPrefixMapping(String prefix) throws SAXException {
  96. throwException("Expat parser should never call this handler method");
  97. }
  98. @Override
  99. public void ignorableWhitespace(char[] ch, int start, int length)
  100. throws SAXException {
  101. contentHandler.ignorableWhitespace(ch, start, length);
  102. }
  103. @Override
  104. public void processingInstruction(String target, String data)
  105. throws SAXException {
  106. contentHandler.processingInstruction(target, data);
  107. }
  108. @Override
  109. public void setDocumentLocator(Locator locator) {
  110. documentLocator = locator;
  111. contentHandler.setDocumentLocator(locator);
  112. }
  113. @Override
  114. public void skippedEntity(String name) throws SAXException {
  115. contentHandler.skippedEntity(name);
  116. }
  117. @Override
  118. public void startDocument() throws SAXException {
  119. contentHandler.startDocument();
  120. }
  121. @Override
  122. public void startElement(String uri, String localName, String qName,
  123. Attributes atts) throws SAXException {
  124. Set<String> mapped = new HashSet<String>();
  125. mappedPrefixes.push(mapped);
  126. AttributesImpl newAtts = new AttributesImpl();
  127. // parse namespace declaration attributes
  128. if (atts != null) {
  129. for (int i = 0; i < atts.getLength(); i++) {
  130. String attQname = atts.getQName(i);
  131. boolean addAtt = true;
  132. if (isNamespaceDeclaration(attQname)) {
  133. String newPrefix = "";
  134. String newUri = atts.getValue(i);
  135. if (attQname.length() > NAMESPACE_DECLARATION_PREFIX
  136. .length()) {
  137. newPrefix = attQname
  138. .substring(NAMESPACE_DECLARATION_PREFIX
  139. .length() + 1);
  140. }
  141. // xmlns prefix cannot be declared
  142. if (NAMESPACE_DECLARATION_PREFIX.equals(newPrefix))
  143. throwException(NAMESPACE_DECLARATION_PREFIX
  144. + " namespace prefix cannot be declared");
  145. // xml cannot be bound to a different uri
  146. if (XML_PREFIX.equals(newPrefix)) {
  147. if (!XML_URI.equals(newUri))
  148. throwException(XML_PREFIX
  149. + " namespace prefix cannot be bound to an URI different from "
  150. + XML_URI);
  151. }
  152. // no prefix except for xml can be bound to xml
  153. // namespace
  154. else if (XML_URI.equals(newUri))
  155. throwException("only " + XML_PREFIX
  156. + " namespace prefix can be bound to "
  157. + XML_URI);
  158. // check that new prefix is an ncname
  159. if (newPrefix.length() > 0
  160. && !XMLChar.isValidNCName(newPrefix))
  161. throwException("namespace prefix " + newPrefix
  162. + " is not an NCName");
  163. if (mapped.contains(newPrefix))
  164. throwException("namespace prefix " + newPrefix
  165. + " declared twice in the same element");
  166. Mapping mapping = new Mapping(newUri, prefixMappings
  167. .get(newPrefix));
  168. prefixMappings.put(newPrefix, mapping);
  169. mapped.add(newPrefix);
  170. contentHandler.startPrefixMapping(newPrefix, newUri);
  171. addAtt = namespacePrefixes;
  172. }
  173. if (addAtt)
  174. newAtts.addAttribute("", "", attQname, atts.getType(i),
  175. atts.getValue(i));
  176. }
  177. }
  178. // set uris on attributes
  179. for (int i = 0; i < newAtts.getLength(); i++) {
  180. String attQname = newAtts.getQName(i);
  181. ExpandedName splitName = parseQName(attQname);
  182. newAtts.setLocalName(i, splitName.localName);
  183. newAtts.setURI(i, splitName.uri);
  184. }
  185. ExpandedName splitName = parseQName(qName);
  186. localName = splitName.localName;
  187. uri = splitName.uri;
  188. contentHandler.startElement(uri, localName, qName, newAtts);
  189. }
  190. @Override
  191. public void startPrefixMapping(String prefix, String uri)
  192. throws SAXException {
  193. throwException("Expat parser should never call this handler method");
  194. }
  195. };
  196. private static class Mapping {
  197. public String uri;
  198. public Mapping next;
  199. public Mapping(String uri, Mapping next) {
  200. this.uri = uri;
  201. this.next = next;
  202. }
  203. }
  204. private static class ExpandedName {
  205. public String uri;
  206. public String localName;
  207. public ExpandedName(String uri, String localName) {
  208. this.uri = uri;
  209. this.localName = localName;
  210. }
  211. }
  212. public ExpatReaderWrapper() throws SAXException {
  213. // initialize with an ExpatReader
  214. XMLReader reader = null;
  215. try {
  216. reader = XMLReaderFactory.createXMLReader("org.apache.harmony.xml.ExpatReader");
  217. }
  218. catch(SAXException ex) {
  219. // expat reader is not available
  220. // fall back to the default XML reader
  221. try {
  222. reader = XMLReaderFactory.createXMLReader();
  223. }
  224. catch(SAXException ex2) {
  225. // no default XML reader is set
  226. // fall back to xmlpull sax2 driver
  227. reader = XMLReaderFactory.createXMLReader("org.xmlpull.v1.sax2.Driver");
  228. }
  229. }
  230. init(reader);
  231. }
  232. public ExpatReaderWrapper(XMLReader expatReader) throws SAXException {
  233. init(expatReader);
  234. }
  235. private void init(XMLReader expatReader) throws SAXException {
  236. if (expatReader == null)
  237. throw new NullPointerException("expatReader cannot be null");
  238. this.expatReader = expatReader;
  239. expatReader.setContentHandler(handler);
  240. expatReader.setFeature(FEATURE_NAMESPACE_PREFIXES, true);
  241. expatReader.setFeature(FEATURE_NAMESPACES, false);
  242. }
  243. @Override
  244. public ContentHandler getContentHandler() {
  245. return contentHandler;
  246. }
  247. @Override
  248. public DTDHandler getDTDHandler() {
  249. return expatReader.getDTDHandler();
  250. }
  251. @Override
  252. public EntityResolver getEntityResolver() {
  253. return expatReader.getEntityResolver();
  254. }
  255. @Override
  256. public ErrorHandler getErrorHandler() {
  257. return expatReader.getErrorHandler();
  258. }
  259. @Override
  260. public boolean getFeature(String name) throws SAXNotRecognizedException,
  261. SAXNotSupportedException {
  262. if (FEATURE_NAMESPACES.equals(name))
  263. return namespaces;
  264. else if (FEATURE_NAMESPACE_PREFIXES.equals(name))
  265. return namespacePrefixes;
  266. else
  267. return expatReader.getFeature(name);
  268. }
  269. @Override
  270. public Object getProperty(String name) throws SAXNotRecognizedException,
  271. SAXNotSupportedException {
  272. return expatReader.getProperty(name);
  273. }
  274. @Override
  275. public void parse(InputSource input) throws IOException, SAXException {
  276. try {
  277. addBaseMappings();
  278. expatReader.parse(input);
  279. } finally {
  280. clearMappings();
  281. }
  282. }
  283. @Override
  284. public void parse(String systemId) throws IOException, SAXException {
  285. try {
  286. addBaseMappings();
  287. expatReader.parse(systemId);
  288. } finally {
  289. clearMappings();
  290. }
  291. }
  292. @Override
  293. public void setContentHandler(ContentHandler handler) {
  294. contentHandler = handler;
  295. }
  296. @Override
  297. public void setDTDHandler(DTDHandler handler) {
  298. expatReader.setDTDHandler(handler);
  299. }
  300. @Override
  301. public void setEntityResolver(EntityResolver resolver) {
  302. expatReader.setEntityResolver(resolver);
  303. }
  304. @Override
  305. public void setErrorHandler(ErrorHandler handler) {
  306. expatReader.setErrorHandler(handler);
  307. }
  308. @Override
  309. public void setFeature(String name, boolean value)
  310. throws SAXNotRecognizedException, SAXNotSupportedException {
  311. if (FEATURE_NAMESPACES.equals(name))
  312. namespaces = value;
  313. else if (FEATURE_NAMESPACE_PREFIXES.equals(name))
  314. namespacePrefixes = value;
  315. else
  316. expatReader.setFeature(name, value);
  317. }
  318. @Override
  319. public void setProperty(String name, Object value)
  320. throws SAXNotRecognizedException, SAXNotSupportedException {
  321. expatReader.setProperty(name, value);
  322. }
  323. private final void clearMappings() {
  324. mappedPrefixes.clear();
  325. prefixMappings.clear();
  326. }
  327. private final void addBaseMappings() {
  328. prefixMappings.put(NAMESPACE_DECLARATION_PREFIX, new Mapping(
  329. NAMESPACE_DECLARATION_URI, null));
  330. prefixMappings.put(XML_PREFIX, new Mapping(XML_URI, null));
  331. }
  332. private final ExpandedName parseQName(String qName) throws SAXException {
  333. String prefix = "";
  334. String uri = "";
  335. String localName = qName;
  336. int commaPos = qName.indexOf(':');
  337. if (commaPos > 0 && commaPos < qName.length() - 1) {
  338. prefix = qName.substring(0, commaPos);
  339. if (!NAMESPACE_DECLARATION_PREFIX.equals(prefix)
  340. && !prefixMappings.containsKey(prefix))
  341. throwException("undeclared namespace prefix " + prefix);
  342. localName = qName.substring(commaPos + 1);
  343. }
  344. // check that localName is an NCName
  345. if (localName.length() > 0 && !XMLChar.isValidNCName(localName))
  346. throwException(localName + " local name is not a valid NCName");
  347. Mapping mapping = prefixMappings.get(prefix);
  348. if (mapping != null)
  349. uri = mapping.uri;
  350. return new ExpandedName(uri, localName);
  351. }
  352. private static final boolean isNamespaceDeclaration(String qName) {
  353. if (qName.startsWith(NAMESPACE_DECLARATION_PREFIX)) {
  354. if (qName.length() == NAMESPACE_DECLARATION_PREFIX.length())
  355. return true;
  356. else if (qName.charAt(NAMESPACE_DECLARATION_PREFIX.length()) == ':') {
  357. if (qName.length() > NAMESPACE_DECLARATION_PREFIX.length() + 1)
  358. return true;
  359. }
  360. }
  361. return false;
  362. }
  363. private final void throwException(String message) throws SAXException {
  364. if (documentLocator != null) {
  365. message = "At line " + documentLocator.getLineNumber()
  366. + ", column " + documentLocator.getColumnNumber() + ": "
  367. + message;
  368. }
  369. throw new SAXException(message);
  370. }
  371. }