1 package org.simantics.xml.sax.base;
\r
3 import java.io.BufferedInputStream;
\r
5 import java.io.FileInputStream;
\r
6 import java.io.Serializable;
\r
7 import java.util.ArrayDeque;
\r
8 import java.util.ArrayList;
\r
9 import java.util.Deque;
\r
10 import java.util.HashMap;
\r
11 import java.util.List;
\r
12 import java.util.Map;
\r
14 import javax.xml.parsers.SAXParser;
\r
15 import javax.xml.parsers.SAXParserFactory;
\r
17 import org.eclipse.core.runtime.IStatus;
\r
18 import org.eclipse.core.runtime.Status;
\r
19 import org.simantics.db.Resource;
\r
20 import org.simantics.db.WriteGraph;
\r
21 import org.simantics.db.exception.DatabaseException;
\r
22 import org.simantics.message.ILogger;
\r
23 import org.xml.sax.Attributes;
\r
24 import org.xml.sax.InputSource;
\r
25 import org.xml.sax.SAXException;
\r
26 import org.xml.sax.XMLReader;
\r
27 import org.xml.sax.helpers.DefaultHandler;
\r
29 public class XMLParser extends DefaultHandler implements Serializable {
\r
31 private static final long serialVersionUID = 7360740940824360338L;
\r
32 private static final boolean debug = false;
\r
33 private static String XML_SCHEMA_REF = "xmlns";
\r
34 private ILogger logger;
\r
35 private Resource root;
\r
36 private String schemaURI;
\r
37 private Deque<Element> current = new ArrayDeque<Element>();
\r
38 private Map<String, XMLElementParser> parsers = new HashMap<String, XMLElementParser>();
\r
39 private WriteGraph graph;
\r
40 private Map<Class<? extends XMLElementParser>, XMLElementParser> namedParsers = new HashMap<Class<? extends XMLElementParser>, XMLElementParser>();
\r
42 private Map<String, XMLParser> subParsers = new HashMap<String, XMLParser>();
\r
44 public XMLParser() {
\r
48 public WriteGraph getGraph() {
\r
52 public void setGraph(WriteGraph graph) {
\r
54 for (XMLParser p : subParsers.values())
\r
58 public String getSchemaURI() {
\r
62 public void setSchemaURI(String schemaURI) {
\r
63 this.schemaURI = schemaURI;
\r
66 public void add(XMLElementParser parser) {
\r
67 if (parser.getElementId() != null)
\r
68 parsers.put(parser.getElementId(), parser);
\r
69 namedParsers.put(parser.getClass(), parser);
\r
72 public void add(XMLParser parser) {
\r
73 subParsers.put(parser.getSchemaURI(), parser);
\r
76 private List<Element> idReferenceElements = new ArrayList<Element>();
\r
77 private void loadElement(Deque<Element> parents, Element element) throws SAXException{
\r
78 XMLElementParser parser = null;
\r
79 Element parent = null;
\r
80 if (parents.size() > 0) {
\r
81 parent = parents.peek();
\r
82 // check for assigned subparser
\r
83 if (parent.getXMLParser() != null && parent.getXMLParser() != this) {
\r
84 element.setXMLParser(parent.getXMLParser());
\r
85 parent.getXMLParser().loadElement(parents, element);
\r
88 if (parent.getElementParser() instanceof XMLElementNamedChildParser) {
\r
89 // use parent's named child parser if it is supported
\r
90 Class<? extends XMLElementParser> parserClass = ((XMLElementNamedChildParser)parent.getElementParser()).getParser(parsers,parent,element);
\r
91 if (parserClass != null) {
\r
92 parser = namedParsers.get(parserClass);
\r
93 if (parser == null) {
\r
95 parser = parserClass.newInstance();
\r
96 namedParsers.put(parserClass, parser);
\r
97 } catch (IllegalAccessException | InstantiationException e) {
\r
98 String err = "Error processing " + parserClass.getName() + " : element parsers must have accessible default constructor";
\r
99 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
\r
100 throw new SAXException(err, e);
\r
104 // if (parser == null && ((XMLElementNamedChildParser)parent.getParser()).useGlobalRules())
\r
105 // parser = parsers.get(element.qName);
\r
107 // otherwise use globally configured element parser
\r
108 parser = parsers.get(element.qName);
\r
111 // use globally configured element parser
\r
112 parser = parsers.get(element.qName);
\r
115 if (parser != null) {
\r
118 element.setData(parser.create(graph, element));
\r
119 if (parser instanceof IDReferenceParser)
\r
120 idReferenceElements.add(element);
\r
121 element.setElementParser(parser);
\r
122 } catch (DatabaseException e) {
\r
123 throw new SAXException(e);
\r
126 // check for schema reference attempt to locate subparser for it.
\r
127 Attribute schemaRef = element.getAttribute(XML_SCHEMA_REF);
\r
128 if (schemaRef != null && subParsers.containsKey(schemaRef.value)) {
\r
129 XMLParser subParser = subParsers.get(schemaRef.value);
\r
130 subParser.loadElement(parents, element);
\r
131 element.setXMLParser(subParser);
\r
133 if (parent == null && parents.size() > 0)
\r
134 parent = parents.peek();
\r
135 String err = "Unknown element " + element.qName + ", parent " + (parent != null ? parent.getQName() : "None");
\r
136 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
\r
137 if (debug) System.err.println(err);
\r
142 private Map<String, Element> idMap = new HashMap<String, Element>();
\r
144 private void handleElement(Deque<Element> parents, Element element) throws SAXException{
\r
145 XMLElementParser parser = element.getElementParser();
\r
146 if (parser != null) {
\r
148 parser.configure(graph, parents, element);
\r
149 if (parents.size() > 0) {
\r
150 Element parent = parents.peek();
\r
151 if (parent.getElementParser() != null) {
\r
152 if (!parent.getElementParser().connectChild(graph, parent, element))
\r
153 if (!parser.connectParent(graph, parent, element)) {
\r
154 String err = "Did not connect element " + element.qName + ", parent " + (parent != null ? parent.getQName() : "None");
\r
155 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
\r
156 if (debug) System.err.println(err);
\r
159 String err = "Did not connect element " + element.qName + ", parent " + (parent != null ? parent.getQName() : "None") + " was not imported";
\r
160 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
\r
161 if (debug) System.err.println(err);
\r
164 String id = parser.getID();
\r
166 Element existing = idMap.put(id, element);
\r
167 if (existing != null) {
\r
168 // report error + use id priorities to select the kept element.
\r
169 boolean useExt = existing.elementParser.idPriority() > element.elementParser.idPriority();
\r
171 idMap.put(id, existing);
\r
172 String err = "Duplicate XML element id: " + id + " for " + element.getQName() + " and " + existing.getQName() + ", using " + (useExt ? existing.getQName() : element.getQName());
\r
173 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
\r
174 if (debug) System.err.println(err);
\r
177 } catch (DatabaseException e) {
\r
178 throw new SAXException(e);
\r
181 Element parent = parents.peek();
\r
182 if (parent != null) {
\r
183 parser = parent.getElementParser();
\r
184 if (parser != null && parser instanceof UnrecognizedElementParser) {
\r
186 ((UnrecognizedElementParser)parser).configureChild(graph, parents, parent, element);
\r
187 } catch (DatabaseException e) {
\r
188 throw new SAXException(e);
\r
195 private void handleCharacters(Element element, String string) throws SAXException{
\r
196 XMLElementParser parser = element.getElementParser();
\r
197 if (parser != null) {
\r
199 parser.configure(graph, element, string);
\r
200 } catch (DatabaseException e) {
\r
201 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, e.getMessage()));
\r
202 throw new SAXException(e);
\r
207 private static String PLUGIN_ID = "org.simantics.xml.sax.base";
\r
209 public void done() throws SAXException{
\r
211 for (Element e : idReferenceElements) {
\r
212 IDReferenceParser parser = (IDReferenceParser)parsers.get(e.qName);
\r
213 if (!parser.connectReferences(graph, e, idMap)) {
\r
214 String err ="Could not resolve ID references for " + e.getQName() + " " + e.getUri();
\r
215 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
\r
216 if (debug) System.err.println(err);
\r
219 } catch (DatabaseException e) {
\r
220 throw new SAXException(e);
\r
224 public Resource getRoot() {
\r
228 StringBuilder charactersValue;
\r
231 public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
\r
232 Element e = new Element(uri,localName,name,attributes);
\r
234 loadElement(current,e);
\r
237 charactersValue = new StringBuilder();
\r
241 public void endElement(String uri, String localName, String name)
\r
242 throws SAXException {
\r
244 if (!current.isEmpty()) {
\r
248 if (charactersValue.length() > 0)
\r
249 handleCharacters(e, charactersValue.toString());
\r
250 handleElement(current,e);
\r
252 if (current.isEmpty()) {
\r
253 root = e.getData();
\r
256 charactersValue = new StringBuilder();
\r
260 public void characters(char[] ch, int start, int length)
\r
261 throws SAXException {
\r
262 charactersValue.append(new String(ch,start,length));
\r
263 // if (!current.isEmpty()) {
\r
264 // String s = new String(ch,start,length);
\r
265 // Element e = current.peek();
\r
266 // handleCharacters(e, s);
\r
271 public void parse(File file, ILogger logger) throws Exception {
\r
272 this.logger = logger;
\r
273 SAXParserFactory spf = SAXParserFactory.newInstance();
\r
274 SAXParser saxParser = spf.newSAXParser();
\r
276 XMLReader reader = saxParser.getXMLReader();
\r
277 reader.setContentHandler(this);
\r
278 reader.parse(new InputSource(new BufferedInputStream(new FileInputStream(file))));
\r