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 public static String PLUGIN_ID = "org.simantics.xml.sax.base";
\r
33 private static final long serialVersionUID = 7360740940824360338L;
\r
34 private static final boolean debug = false;
\r
35 private static String XML_SCHEMA_REF = "xmlns";
\r
36 private ILogger logger;
\r
37 private WriteGraph graph;
\r
38 private Resource root;
\r
39 private String schemaURI;
\r
40 private Deque<ParserElement> current = new ArrayDeque<ParserElement>();
\r
41 private Map<String, XMLElementParser> parsers = new HashMap<String, XMLElementParser>();
\r
42 private Map<Class<? extends XMLElementParser>, XMLElementParser> namedParsers = new HashMap<Class<? extends XMLElementParser>, XMLElementParser>();
\r
43 private Map<String, XMLParser> subParsers = new HashMap<String, XMLParser>();
\r
45 public XMLParser() {
\r
49 public WriteGraph getGraph() {
\r
53 public void setGraph(WriteGraph graph) {
\r
55 for (XMLParser p : subParsers.values())
\r
59 public String getSchemaURI() {
\r
63 public void setSchemaURI(String schemaURI) {
\r
64 this.schemaURI = schemaURI;
\r
67 public void add(XMLElementParser parser) {
\r
68 if (parser.getElementId() != null)
\r
69 parsers.put(parser.getElementId(), parser);
\r
70 namedParsers.put(parser.getClass(), parser);
\r
73 public void add(XMLParser parser) {
\r
74 subParsers.put(parser.getSchemaURI(), parser);
\r
77 private List<ParserElement> idReferenceElements = new ArrayList<ParserElement>();
\r
78 private void loadElement(Deque<ParserElement> parents, ParserElement element) throws SAXException{
\r
79 XMLElementParser parser = null;
\r
80 ParserElement parent = null;
\r
81 if (parents.size() > 0) {
\r
82 parent = parents.peek();
\r
83 // check for assigned subparser
\r
84 if (parent.getXMLParser() != null && parent.getXMLParser() != this) {
\r
85 element.setXMLParser(parent.getXMLParser());
\r
86 parent.getXMLParser().loadElement(parents, element);
\r
89 if (parent.getElementParser() instanceof XMLElementNamedChildParser) {
\r
90 // use parent's named child parser if it is supported
\r
91 Class<? extends XMLElementParser> parserClass = ((XMLElementNamedChildParser)parent.getElementParser()).getParser(parsers,parent,element);
\r
92 if (parserClass != null) {
\r
93 parser = namedParsers.get(parserClass);
\r
94 if (parser == null) {
\r
96 parser = parserClass.newInstance();
\r
97 namedParsers.put(parserClass, parser);
\r
98 } catch (IllegalAccessException | InstantiationException e) {
\r
99 String err = "Error processing " + parserClass.getName() + " : element parsers must have accessible default constructor";
\r
100 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
\r
101 throw new SAXException(err, e);
\r
105 // if (parser == null && ((XMLElementNamedChildParser)parent.getParser()).useGlobalRules())
\r
106 // parser = parsers.get(element.qName);
\r
108 // otherwise use globally configured element parser
\r
109 parser = parsers.get(element.qName);
\r
112 // use globally configured element parser
\r
113 parser = parsers.get(element.qName);
\r
116 if (parser != null) {
\r
119 element.setData(parser.create(graph, element));
\r
120 if (parser instanceof IDReferenceParser)
\r
121 idReferenceElements.add(element);
\r
122 element.setElementParser(parser);
\r
123 } catch (DatabaseException e) {
\r
124 throw new SAXException(e);
\r
127 // check for schema reference attempt to locate subparser for it.
\r
128 Attribute schemaRef = element.getAttribute(XML_SCHEMA_REF);
\r
129 if (schemaRef != null && subParsers.containsKey(schemaRef.value)) {
\r
130 XMLParser subParser = subParsers.get(schemaRef.value);
\r
131 subParser.loadElement(parents, element);
\r
132 element.setXMLParser(subParser);
\r
134 if (parent == null && parents.size() > 0)
\r
135 parent = parents.peek();
\r
136 String err = "Unknown element " + element.qName + ", parent " + (parent != null ? parent.getQName() : "None");
\r
137 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
\r
138 if (debug) System.err.println(err);
\r
143 private Map<String, ParserElement> idMap = new HashMap<String, ParserElement>();
\r
145 private void handleElement(Deque<ParserElement> parents, ParserElement element) throws SAXException{
\r
146 XMLElementParser parser = element.getElementParser();
\r
147 if (parser != null) {
\r
149 parser.configure(graph, parents, element);
\r
150 if (parents.size() > 0) {
\r
151 ParserElement parent = parents.peek();
\r
152 if (parent.getElementParser() != null) {
\r
153 if (!parent.getElementParser().connectChild(graph, parent, element))
\r
154 if (!parser.connectParent(graph, parent, element)) {
\r
155 String err = "Did not connect element " + element.qName + ", parent " + (parent != null ? parent.getQName() : "None");
\r
156 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
\r
157 if (debug) System.err.println(err);
\r
160 String err = "Did not connect element " + element.qName + ", parent " + (parent != null ? parent.getQName() : "None") + " was not imported";
\r
161 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
\r
162 if (debug) System.err.println(err);
\r
165 String id = parser.getID();
\r
167 ParserElement existing = idMap.put(id, element);
\r
168 if (existing != null) {
\r
169 // report error + use id priorities to select the kept element.
\r
170 boolean useExt = existing.elementParser.idPriority() > element.elementParser.idPriority();
\r
172 idMap.put(id, existing);
\r
173 String err = "Duplicate XML element id: " + id + " for " + element.getQName() + " and " + existing.getQName() + ", using " + (useExt ? existing.getQName() : element.getQName());
\r
174 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
\r
175 if (debug) System.err.println(err);
\r
178 } catch (DatabaseException e) {
\r
179 throw new SAXException(e);
\r
182 ParserElement parent = parents.peek();
\r
183 if (parent != null) {
\r
184 parser = parent.getElementParser();
\r
185 if (parser != null && parser instanceof UnrecognizedElementParser) {
\r
187 ((UnrecognizedElementParser)parser).configureChild(graph, parents, parent, element);
\r
188 } catch (DatabaseException e) {
\r
189 throw new SAXException(e);
\r
196 private void handleCharacters(ParserElement element, String string) throws SAXException{
\r
197 XMLElementParser parser = element.getElementParser();
\r
198 if (parser != null) {
\r
200 parser.configure(graph, element, string);
\r
201 } catch (DatabaseException e) {
\r
202 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, e.getMessage()));
\r
203 throw new SAXException(e);
\r
210 public void done() throws SAXException{
\r
212 for (ParserElement e : idReferenceElements) {
\r
213 IDReferenceParser parser = (IDReferenceParser)parsers.get(e.qName);
\r
214 if (!parser.connectReferences(graph, e, idMap)) {
\r
215 String err ="Could not resolve ID references for " + e.getQName() + " " + e.getUri();
\r
216 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
\r
217 if (debug) System.err.println(err);
\r
220 } catch (DatabaseException e) {
\r
221 throw new SAXException(e);
\r
225 public Resource getRoot() {
\r
229 StringBuilder charactersValue;
\r
232 public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
\r
233 ParserElement e = new ParserElement(uri,localName,name,attributes);
\r
235 loadElement(current,e);
\r
238 charactersValue = new StringBuilder();
\r
242 public void endElement(String uri, String localName, String name)
\r
243 throws SAXException {
\r
244 ParserElement e = null;
\r
245 if (!current.isEmpty()) {
\r
249 if (charactersValue.length() > 0)
\r
250 handleCharacters(e, charactersValue.toString());
\r
251 handleElement(current,e);
\r
253 if (current.isEmpty()) {
\r
254 root = e.getData();
\r
257 charactersValue = new StringBuilder();
\r
261 public void characters(char[] ch, int start, int length)
\r
262 throws SAXException {
\r
263 charactersValue.append(new String(ch,start,length));
\r
264 // if (!current.isEmpty()) {
\r
265 // String s = new String(ch,start,length);
\r
266 // Element e = current.peek();
\r
267 // handleCharacters(e, s);
\r
272 public void parse(File file, ILogger logger) throws Exception {
\r
273 this.logger = logger;
\r
274 SAXParserFactory spf = SAXParserFactory.newInstance();
\r
275 SAXParser saxParser = spf.newSAXParser();
\r
277 XMLReader reader = saxParser.getXMLReader();
\r
278 reader.setContentHandler(this);
\r
279 reader.parse(new InputSource(new BufferedInputStream(new FileInputStream(file))));
\r