1 package org.simantics.xml.sax.base;
3 import java.io.BufferedInputStream;
5 import java.io.FileInputStream;
6 import java.io.Serializable;
7 import java.util.ArrayDeque;
8 import java.util.ArrayList;
9 import java.util.Deque;
10 import java.util.HashMap;
11 import java.util.List;
14 import javax.xml.parsers.SAXParser;
15 import javax.xml.parsers.SAXParserFactory;
17 import org.eclipse.core.runtime.IStatus;
18 import org.eclipse.core.runtime.Status;
19 import org.simantics.db.Resource;
20 import org.simantics.db.WriteGraph;
21 import org.simantics.db.exception.DatabaseException;
22 import org.simantics.message.ILogger;
23 import org.xml.sax.Attributes;
24 import org.xml.sax.InputSource;
25 import org.xml.sax.SAXException;
26 import org.xml.sax.XMLReader;
27 import org.xml.sax.helpers.DefaultHandler;
29 public class XMLParser extends DefaultHandler implements Serializable {
31 public static String PLUGIN_ID = "org.simantics.xml.sax.base";
32 public static String XML_NAMESPACE_REF = "xmlns";
34 private static final long serialVersionUID = 7360740940824360338L;
35 private static final boolean debug = false;
37 private ILogger logger;
38 private WriteGraph graph;
39 private Resource root;
40 private String schemaURI;
41 private Deque<ParserElement> current = new ArrayDeque<ParserElement>();
42 private Map<String, XMLElementParser> parsers = new HashMap<String, XMLElementParser>();
43 private Map<Class<? extends XMLElementParser>, XMLElementParser> namedParsers = new HashMap<Class<? extends XMLElementParser>, XMLElementParser>();
44 private Map<String, XMLParser> subParsers = new HashMap<String, XMLParser>();
50 public WriteGraph getGraph() {
54 public void setGraph(WriteGraph graph) {
56 for (XMLParser p : subParsers.values())
60 public String getSchemaURI() {
64 public void setSchemaURI(String schemaURI) {
65 this.schemaURI = schemaURI;
68 public void add(XMLElementParser parser) {
69 if (parser.getElementId() != null)
70 parsers.put(parser.getElementId(), parser);
71 namedParsers.put(parser.getClass(), parser);
74 public void add(XMLParser parser) {
75 subParsers.put(parser.getSchemaURI(), parser);
78 private List<ParserElement> idReferenceElements = new ArrayList<ParserElement>();
79 private void loadElement(Deque<ParserElement> parents, ParserElement element) throws SAXException{
80 XMLElementParser parser = null;
81 ParserElement parent = null;
82 if (parents.size() > 0) {
83 parent = parents.peek();
84 // check for assigned subparser
85 if (parent.getXMLParser() != null && parent.getXMLParser() != this) {
86 element.setXMLParser(parent.getXMLParser());
87 parent.getXMLParser().loadElement(parents, element);
90 if (parent.getElementParser() instanceof XMLElementNamedChildParser) {
91 // use parent's named child parser if it is supported
92 Class<? extends XMLElementParser> parserClass = ((XMLElementNamedChildParser)parent.getElementParser()).getParser(parsers,parent,element);
93 if (parserClass != null) {
94 parser = namedParsers.get(parserClass);
97 parser = parserClass.newInstance();
98 namedParsers.put(parserClass, parser);
99 } catch (IllegalAccessException | InstantiationException e) {
100 String err = "Error processing " + parserClass.getName() + " : element parsers must have accessible default constructor";
101 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
102 throw new SAXException(err, e);
106 // if (parser == null && ((XMLElementNamedChildParser)parent.getParser()).useGlobalRules())
107 // parser = parsers.get(element.qName);
109 // otherwise use globally configured element parser
110 parser = parsers.get(element.qName);
113 // use globally configured element parser
114 parser = parsers.get(element.qName);
117 if (parser != null) {
120 element.setData(parser.create(graph, element));
121 if (parser instanceof IDReferenceParser)
122 idReferenceElements.add(element);
123 element.setElementParser(parser);
124 } catch (DatabaseException e) {
125 throw new SAXException(e);
128 // check for schema reference attempt to locate subparser for it.
129 Attribute schemaRef = element.getAttribute(XML_NAMESPACE_REF);
130 if (schemaRef != null && subParsers.containsKey(schemaRef.value)) {
131 XMLParser subParser = subParsers.get(schemaRef.value);
132 subParser.loadElement(parents, element);
133 element.setXMLParser(subParser);
135 if (parent == null && parents.size() > 0)
136 parent = parents.peek();
139 err = "Unknown element " + element.qName + ", parent " + (parent != null ? parent.getQName() : "None");
141 err = "Unknown root element " + element.qName + ", cannot import the file";
142 throw new SAXException(err);
145 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
146 if (debug) System.err.println(err);
151 private Map<String, ParserElement> idMap = new HashMap<String, ParserElement>();
153 private void handleElement(Deque<ParserElement> parents, ParserElement element) throws SAXException{
154 XMLElementParser parser = element.getElementParser();
155 if (parser != null) {
157 parser.configure(graph, parents, element);
158 if (parents.size() > 0) {
159 ParserElement parent = parents.peek();
160 if (parent.getElementParser() != null) {
161 if (!parent.getElementParser().connectChild(graph, parent, element))
162 if (!parser.connectParent(graph, parent, element)) {
163 String err = "Did not connect element " + element.qName + ", parent " + (parent != null ? parent.getQName() : "None");
164 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
165 if (debug) System.err.println(err);
168 String err = "Did not connect element " + element.qName + ", parent " + (parent != null ? parent.getQName() : "None") + " was not imported";
169 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
170 if (debug) System.err.println(err);
173 String id = parser.getID();
175 ParserElement existing = idMap.put(id, element);
176 if (existing != null) {
177 // report error + use id priorities to select the kept element.
178 boolean useExt = existing.elementParser.idPriority() > element.elementParser.idPriority();
180 idMap.put(id, existing);
181 String err = "Duplicate XML element id: " + id + " for " + element.getQName() + " and " + existing.getQName() + ", using " + (useExt ? existing.getQName() : element.getQName());
182 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
183 if (debug) System.err.println(err);
186 } catch (DatabaseException e) {
187 throw new SAXException(e);
190 ParserElement parent = parents.peek();
191 if (parent != null) {
192 parser = parent.getElementParser();
193 if (parser != null && parser instanceof UnrecognizedElementParser) {
195 ((UnrecognizedElementParser)parser).configureChild(graph, parents, parent, element);
196 } catch (DatabaseException e) {
197 throw new SAXException(e);
203 element.createLists(graph);
204 } catch (DatabaseException e) {
205 throw new SAXException(e);
209 private void handleCharacters(ParserElement element, String string) throws SAXException{
210 XMLElementParser parser = element.getElementParser();
211 if (parser != null) {
213 parser.configure(graph, element, string);
214 } catch (DatabaseException e) {
215 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, e.getMessage()));
216 throw new SAXException(e);
223 public void done() throws SAXException{
225 for (ParserElement e : idReferenceElements) {
226 IDReferenceParser parser = (IDReferenceParser)parsers.get(e.qName);
227 if (!parser.connectReferences(graph, e, idMap)) {
228 String err ="Could not resolve ID references for " + e.getQName() + " " + e.getUri();
229 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
230 if (debug) System.err.println(err);
233 } catch (DatabaseException e) {
234 throw new SAXException(e);
238 public Resource getRoot() {
242 StringBuilder charactersValue;
245 public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {
246 ParserElement e = new ParserElement(uri,localName,name,attributes);
248 loadElement(current,e);
251 charactersValue = new StringBuilder();
255 public void endElement(String uri, String localName, String name)
256 throws SAXException {
257 ParserElement e = null;
258 if (!current.isEmpty()) {
262 handleCharacters(e, charactersValue.toString());
263 handleElement(current,e);
265 if (current.isEmpty()) {
269 charactersValue = new StringBuilder();
273 public void characters(char[] ch, int start, int length)
274 throws SAXException {
275 charactersValue.append(new String(ch,start,length));
276 // if (!current.isEmpty()) {
277 // String s = new String(ch,start,length);
278 // Element e = current.peek();
279 // handleCharacters(e, s);
284 public void parse(File file, ILogger logger) throws Exception {
285 this.logger = logger;
286 SAXParserFactory spf = SAXParserFactory.newInstance();
287 SAXParser saxParser = spf.newSAXParser();
289 XMLReader reader = saxParser.getXMLReader();
290 reader.setContentHandler(this);
291 reader.parse(new InputSource(new BufferedInputStream(new FileInputStream(file))));