]> gerrit.simantics Code Review - simantics/interop.git/blob - org.simantics.xml.sax.base/src/org/simantics/xml/sax/base/XMLParser.java
Make it possible to customise element parsers before imports
[simantics/interop.git] / org.simantics.xml.sax.base / src / org / simantics / xml / sax / base / XMLParser.java
1 package org.simantics.xml.sax.base;
2
3 import java.io.BufferedInputStream;
4 import java.io.File;
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;
12 import java.util.Map;
13
14 import javax.xml.parsers.SAXParser;
15 import javax.xml.parsers.SAXParserFactory;
16
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;
28
29 public class XMLParser extends DefaultHandler implements Serializable {
30
31         public static String PLUGIN_ID = "org.simantics.xml.sax.base";
32         public static String XML_NAMESPACE_REF = "xmlns";
33         
34         private static final long serialVersionUID = 7360740940824360338L;
35         private static final boolean debug = false;
36         
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>();
45         
46         public XMLParser() {
47         
48         }
49         
50         public WriteGraph getGraph() {
51                 return graph;
52         }
53         
54         public void setGraph(WriteGraph graph) {
55                 if (graph == this.graph)
56                         return;
57                 this.graph = graph;
58                 for (XMLParser p : subParsers.values())
59                         p.setGraph(graph);
60         }
61         
62         public void setLogger(ILogger logger) {
63                 if (logger == this.logger)
64                         return;
65                 this.logger = logger;
66                 for (XMLParser p : subParsers.values())
67                         p.setLogger(logger);
68         }
69         
70         public String getSchemaURI() {
71                 return schemaURI;
72         }
73         
74         public void setSchemaURI(String schemaURI) {
75                 this.schemaURI = schemaURI;
76         }
77         
78         public void add(XMLElementParser parser) {
79                 if (parser.getElementId() != null)
80                         parsers.put(parser.getElementId(), parser);
81                 namedParsers.put(parser.getClass(), parser);
82         }
83         
84         public void add(XMLParser parser) {
85                 subParsers.put(parser.getSchemaURI(), parser);
86                 
87         }
88         
89         public XMLParser resolveDependencies() {
90
91                 java.util.Map<String, XMLParser> map = new java.util.HashMap<>();
92                 map.put(this.getSchemaURI(), this);
93                 addDependencies(map);
94                 return this;
95         }
96         
97         public void addDependencies(java.util.Map<String,XMLParser> map) {
98                 
99         }
100         
101         private List<ParserElement> idReferenceElements = new ArrayList<ParserElement>();
102         
103         private boolean loadElement(Deque<ParserElement> parents, ParserElement element) throws SAXException{
104                 return loadElement(parents, element, true);
105         }
106         private boolean loadElement(Deque<ParserElement> parents, ParserElement element, boolean checkParent) throws SAXException{              
107                 XMLElementParser parser = null;
108                 ParserElement parent = null;
109                 if (parents.size() > 0) {
110                         // process a child element
111                         parent = parents.peek();
112                         // check for assigned subparser
113                         if (checkParent && parent.getXMLParser() != null && parent.getXMLParser() != this) {
114                                 //element.setXMLParser(parent.getXMLParser());
115                                 element.setXMLParser(parent.getXMLParser());
116                                 if (parent.getXMLParser().loadElement(parents, element))
117                                         return true;
118                         }
119                         if (parent.getElementParser() instanceof XMLElementNamedChildParser) {
120                                 // use parent's named child parser if it is supported
121                                 Class<? extends XMLElementParser> parserClass = ((XMLElementNamedChildParser)parent.getElementParser()).getParser(parsers,parent,element);
122                                 if (parserClass != null) {
123                                         parser = namedParsers.get(parserClass);
124                                         if (parser == null) {
125                                                 try {
126                                                         parser = parserClass.newInstance();
127                                                         namedParsers.put(parserClass, parser);
128                                                 } catch (IllegalAccessException | InstantiationException e) {
129                                                         String err = "Error processing " + parserClass.getName() + " : element parsers must have accessible default constructor";
130                                                         logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
131                                                         throw new SAXException(err, e);
132                                                 }
133                                         }
134                                 } 
135 //                              if (parser == null && ((XMLElementNamedChildParser)parent.getParser()).useGlobalRules())
136 //                                      parser = parsers.get(element.qName);
137                         } else {
138                                 // otherwise use globally configured element parser
139                                 parser = parsers.get(element.getLocalName());
140                         }
141                 } else {
142                         // use globally configured element parser for a root element
143                         parser = parsers.get(element.getLocalName());
144                 }
145                 
146                 if (parser != null) {
147                         try {
148                                 
149                                 element.setData(parser.create(graph, element));
150                                 if (parser instanceof IDReferenceParser)
151                                         idReferenceElements.add(element);
152                                 element.setElementParser(parser);
153                                 return true;
154                         } catch (DatabaseException e) {
155                                 throw new SAXException(e);
156                         }
157                 } else {
158                         // check for schema reference attempt to locate subparser for it.
159                         Attribute schemaRef = element.getAttribute(XML_NAMESPACE_REF);
160                         String nsRef[] = element.getQName().split(":");
161                         if (schemaRef != null && subParsers.containsKey(schemaRef.value)) {
162                                 XMLParser subParser = subParsers.get(schemaRef.value);
163                                 boolean b = subParser.loadElement(parents, element);
164                                 element.setXMLParser(subParser);
165                                 return b;
166                         } else if (nsRef.length == 2 && element.getNS(nsRef[0]) != null && subParsers.containsKey(element.getNS(nsRef[0]))) {
167                                 XMLParser subParser = subParsers.get(element.getNS(nsRef[0]));
168                                 boolean b = subParser.loadElement(parents, element, false);
169                                 element.setXMLParser(subParser);
170                                 return b;
171                         } else {
172                                 if (parent == null && parents.size() > 0)
173                                         parent = parents.peek();
174                                 String err = null;
175                                 if (parent != null)
176                                         err = "Unknown element " + element.getLocalName() + ", parent " + (parent != null ? parent.getLocalName() : "None");
177                                 else {
178                                         err = "Unknown root element " + element.getLocalName() + ", cannot import the file";
179                                         throw new SAXException(err);
180                                 }
181                                 
182                                 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
183                                 if (debug) System.err.println(err);
184                                 return false;
185                         }
186                 }
187         }
188         
189         private Map<String, ParserElement> idMap = new HashMap<String, ParserElement>();
190         
191         private void handleElement(Deque<ParserElement> parents, ParserElement element) throws SAXException{
192                 XMLElementParser parser = element.getElementParser();
193                 if (parser != null) {
194                         try {
195                                 parser.configure(graph, parents, element);
196                                 if (parents.size() > 0) {
197                                         ParserElement parent = parents.peek();
198                                         if (parent.getElementParser() != null) {
199                                                 if (!parent.getElementParser().connectChild(graph, parent, element))
200                                                         if (!parser.connectParent(graph, parent, element)) {
201                                                                 String err = "Did not connect element " + element.getLocalName() + ", parent " + (parent != null ? parent.getLocalName() : "None");
202                                                                 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
203                                                                 if (debug) System.err.println(err);
204                                                         }
205                                         } else {
206                                                 String err = "Did not connect element " + element.getLocalName() + ", parent " + (parent != null ? parent.getLocalName() : "None") + " was not imported";
207                                                 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
208                                                 if (debug) System.err.println(err);
209                                         }
210                                 }
211                                 String id = parser.getID();
212                                 if (id != null) {
213                                         ParserElement existing = idMap.put(id, element);
214                                         if (existing != null) {
215                                                 // report error + use id priorities to select the kept element.
216                                                 boolean useExt = existing.elementParser.idPriority() > element.elementParser.idPriority();
217                                                 if (useExt)
218                                                         idMap.put(id, existing);
219                                                 String err = "Duplicate XML element id: " + id + " for " + element.getLocalName() + " and " + existing.getLocalName() + ", using " + (useExt ? existing.getLocalName() : element.getLocalName());
220                                                 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
221                                                 if (debug) System.err.println(err);
222                                         }
223                                 }
224                         } catch (DatabaseException e) {
225                                 throw new SAXException(e);
226                         }
227                 } else {
228                         ParserElement parent = parents.peek();
229                         if (parent != null) { 
230                                 parser = parent.getElementParser();
231                                 if (parser != null && parser instanceof UnrecognizedElementParser) {
232                                         try {
233                                                 ((UnrecognizedElementParser)parser).configureChild(graph, parents, parent, element);
234                                         } catch (DatabaseException e) {
235                                                 throw new SAXException(e);
236                                         }
237                                 }
238                         }
239                 }
240                 try {
241                         element.createLists(graph);
242                 } catch (DatabaseException e) {
243                         throw new SAXException(e);
244                 }
245         }
246         
247         private void handleCharacters(ParserElement element, String string) throws SAXException{
248                 XMLElementParser parser = element.getElementParser();
249                 if (parser != null) {
250                         try {
251                                 parser.configure(graph, element, string);
252                         } catch (DatabaseException e) {
253                                 logger.log(new Status(IStatus.ERROR, PLUGIN_ID, e.getMessage()));
254                                 throw new SAXException(e);
255                         }
256                 }
257         }
258         
259         
260         
261         public void done() throws SAXException{
262                 try {
263                         for (ParserElement e : idReferenceElements) {
264                                 IDReferenceParser parser = (IDReferenceParser)parsers.get(e.getLocalName());
265                                 if (!parser.connectReferences(graph, e, idMap)) {
266                                         String err ="Could not resolve ID references for " + e.getLocalName() + " " + e.getUri();
267                                         logger.log(new Status(IStatus.ERROR, PLUGIN_ID, err));
268                                         if (debug) System.err.println(err);
269                                 }
270                         }
271                 } catch (DatabaseException e) {
272                         throw new SAXException(e);
273                 }
274         }
275         
276         public Resource getRoot() {
277                 return root;
278         }
279         
280         StringBuilder charactersValue;
281         
282         @Override
283         public void startElement(String uri, String localName, String name,     Attributes attributes) throws SAXException {
284                 ParserElement e = new ParserElement(current.peek(),uri,localName,name,attributes);
285                 
286                 loadElement(current,e);
287                 current.push(e);
288                 
289                 charactersValue = new StringBuilder();
290         }
291         
292         @Override
293         public void endElement(String uri, String localName, String name)
294                         throws SAXException {
295                 ParserElement e = null;
296                 if (!current.isEmpty()) {
297                         e = current.pop();
298                 }
299                 if (e != null) {
300                         handleCharacters(e, charactersValue.toString());
301                         handleElement(current,e);
302                         if (current.isEmpty()) {
303                                 root = e.getData();
304                         }
305                 }
306                 
307                 
308                 charactersValue = new StringBuilder();
309         }
310         
311         @Override
312         public void characters(char[] ch, int start, int length)
313                         throws SAXException {
314                 charactersValue.append(new String(ch,start,length));
315 //              if (!current.isEmpty()) {
316 //                      String s = new String(ch,start,length);
317 //                      Element e = current.peek();
318 //                      handleCharacters(e, s);
319 //              }
320                 
321         }
322         
323         public void parse(File file, ILogger logger) throws Exception {
324                 setLogger(logger);
325                 SAXParserFactory spf = SAXParserFactory.newInstance();
326                 SAXParser saxParser = spf.newSAXParser();
327                 
328                 XMLReader reader = saxParser.getXMLReader();
329                 reader.setContentHandler(this);
330                 reader.parse(new InputSource(new BufferedInputStream(new FileInputStream(file))));
331                 done();
332         }
333         
334         public XMLElementParser getParser(String elementId) {
335                 return parsers.get(elementId);
336         }
337
338         public void setParser(String elementId, XMLElementParser p) {
339                 parsers.put(elementId, p);
340         }
341
342         
343         
344 }