2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2010 - Calixte DENIZET
5 * This file must be used under the terms of the CeCILL.
6 * This source file is licensed as described in the file COPYING, which
7 * you should have received as part of this distribution. The terms
8 * are also available at
9 * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
13 package org.scilab.modules.helptools;
16 import java.io.IOException;
17 import java.lang.reflect.InvocationTargetException;
18 import java.lang.reflect.Method;
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 import java.util.List;
23 import java.util.Stack;
25 import javax.xml.parsers.ParserConfigurationException;
26 import javax.xml.parsers.SAXParser;
27 import javax.xml.parsers.SAXParserFactory;
29 import org.xml.sax.Attributes;
30 import org.xml.sax.InputSource;
31 import org.xml.sax.Locator;
32 import org.xml.sax.SAXException;
33 import org.xml.sax.helpers.DefaultHandler;
35 import org.scilab.modules.helptools.external.ExternalXMLHandler;
36 import org.scilab.modules.helptools.external.HTMLMathMLHandler;
37 import org.scilab.modules.helptools.external.HTMLScilabHandler;
38 import org.scilab.modules.helptools.external.HTMLSVGHandler;
41 * Class the convert a DocBook xml file
42 * @author Calixte DENIZET
44 public class DocbookTagConverter extends DefaultHandler {
46 private static final String DOCBOOKURI = "http://docbook.org/ns/docbook";
47 private static final Class[] argsType = new Class[] {Map.class, String.class};
49 private Map<String, Method> mapMeth = new HashMap<String, Method>();
50 private Map<String, ExternalXMLHandler> externalHandlers = new HashMap<String, ExternalXMLHandler>();
51 private List<DocbookTagConverter> converters;
52 private final File in;
53 private DocbookElement baseElement = new DocbookElement(null, null, null);
54 private Stack<DocbookElement> stack = new Stack<DocbookElement>();
55 private String errors = "";
58 * True when an error is met during the parsing
60 protected boolean hasError;
63 * The file which is parsed
65 protected String currentFileName;
68 * The file which is parsed
70 protected String currentBaseName;
73 * Useful to locate the errors
75 protected Locator locator;
79 * @param in the input file path
81 public DocbookTagConverter(String in) throws IOException {
82 if (in != null && !in.isEmpty()) {
83 this.in = new File(in);
91 * @param in the inputstream
92 * @param inName the name of the input file
94 public DocbookTagConverter(String inName, DocbookElement elem) throws IOException {
101 * @param tag the tag to handle
102 * @param attributes the attributes as a Map
103 * @param contents the contents between the matching tags
104 * @return the HTML code
105 * @throws UnhandledDocbookTagException if an handled tga is encountered
107 public String handleDocbookTag(String tag, Map attributes, String contents) throws SAXException {
108 if (tag != null && tag.length() > 0) {
109 Method method = mapMeth.get(tag);
110 if (method == null) {
111 String name = "handle" + Character.toString(Character.toUpperCase(tag.charAt(0))) + tag.substring(1);
113 method = this.getClass().getMethod(name, argsType);
114 mapMeth.put(tag, method);
115 } catch (NoSuchMethodException e) {
116 throw new UnhandledDocbookTagException(tag);
120 return (String) method.invoke(this, new Object[] {attributes, contents});
121 } catch (IllegalAccessException e) {
122 throw new UnhandledDocbookTagException(tag);
123 } catch (InvocationTargetException e) {
124 throw new SAXException("Problem with tag " + tag + "\n" + e.getCause());
127 throw new UnhandledDocbookTagException(tag);
131 public String getCurrentFileName() {
132 return currentFileName;
135 public String getCurrentBaseName() {
136 return currentBaseName;
140 * Register an Docbook tag converter. The aim is to parse the xml one time.
141 * @param c the converter to register
143 public void registerConverter(DocbookTagConverter c) {
144 if (converters == null) {
145 converters = new ArrayList<DocbookTagConverter>();
149 c.setDocumentLocator(locator);
153 * @param c the converter to remove
155 public void removeConverter(DocbookTagConverter c) {
156 if (converters != null) {
157 converters.remove(c);
162 * @return all the registered converters
164 public DocbookTagConverter[] getConverters() {
165 if (converters == null) {
169 return converters.toArray(new DocbookTagConverter[0]);
173 * Register an XMLHandler for external XML
174 * @param h the external XML handler
176 public void registerExternalXMLHandler(ExternalXMLHandler h) {
177 if (externalHandlers.get(h.getURI()) == null) {
178 externalHandlers.put(h.getURI(), h);
179 h.setConverter(this);
184 * @param tagName the tag name
185 * @return true if the contents of the tag must be escaped
187 public boolean isEscapable(String tagName, String uri) {
192 * @param tagName the tag name
193 * @return true if the contents of the tag must be trimed
195 public boolean isTrimable(String tagName) {
200 * @return the parent tag name
202 public String getParentTagName() {
203 return stack.peek().getName();
207 * @return the parent tag contents
209 public String getParentContent() {
210 return stack.peek().getStringBuilder().toString();
214 * Start the conversion
215 * @throws SAXException if a problem is encountered during the parsing
216 * @throws IOException if an IO problem is encountered
218 public void convert() throws SAXException, IOException {
219 SAXParserFactory factory = SAXParserFactory.newInstance();
220 factory.setValidating(false);
221 factory.setNamespaceAware(true);
224 factory.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
225 SAXParser parser = factory.newSAXParser();
226 // Must be uncommented to be able to read comments
227 //parser.setProperty("http://xml.org/sax/properties/lexical-handler", this);
228 parser.parse(in, this);
229 } catch (ParserConfigurationException e) {
230 exceptionOccurred(e);
231 } catch (SAXException e) {
232 System.err.println(e);
233 } catch (IOException e) {
234 System.err.println(e);
238 throw new SAXException(errors);
245 public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
246 currentFileName = systemId;
247 currentBaseName = new File(systemId).getName();
248 HTMLMathMLHandler.getInstance().resetCompt();
249 HTMLSVGHandler.getInstance().resetCompt();
250 HTMLScilabHandler.getInstance().resetCompt();
251 if (converters != null) {
252 for (DocbookTagConverter conv : converters) {
253 conv.resolveEntity(publicId, systemId);
257 return super.resolveEntity(publicId, systemId);
263 public void startDocument() throws SAXException {
264 stack.push(baseElement.getNewInstance(null, null, null));
265 if (converters != null) {
266 for (DocbookTagConverter conv : converters) {
267 conv.startDocument();
275 public void endDocument() throws SAXException {
278 if (converters != null) {
279 for (DocbookTagConverter conv : converters) {
288 public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
289 if (uri.equals(DOCBOOKURI)) {
290 int len = attributes.getLength();
291 Map<String, String> map = new HashMap<String, String>(len);
292 for (int i = 0; i < len; i++) {
293 map.put(attributes.getLocalName(i), attributes.getValue(i));
295 stack.push(baseElement.getNewInstance(localName, uri, map));
297 ExternalXMLHandler h = externalHandlers.get(uri);
299 exceptionOccurred(new SAXException("uri " + uri + " not handled"));
302 StringBuilder buf = h.startExternalXML(localName, attributes, locator);
304 DocbookElement elem = baseElement.getNewInstance(localName, uri, null);
305 elem.setStringBuilder(buf);
310 if (converters != null) {
311 for (DocbookTagConverter conv : converters) {
312 conv.startElement(uri, localName, qName, attributes);
320 public void endElement(String uri, String localName, String qName) throws SAXException {
321 if (uri.equals(DOCBOOKURI)) {
322 DocbookElement elem = stack.pop();
323 if (!elem.getName().equals(localName)) {
324 exceptionOccurred(new SAXException("tag " + elem.getName() + " is closed with tag " + localName));
328 DocbookElement elemp = stack.peek();
329 elemp.setParent(elem);
330 StringBuilder buf = elem.getStringBuilder();
331 if (isTrimable(elem.getName())) {
334 String str = handleDocbookTag(elem.getName(), elem.getAttributes(), buf.toString());
336 elemp.getStringBuilder().append(str);
338 } catch (SAXException e) {
339 exceptionOccurred(e);
343 ExternalXMLHandler h = externalHandlers.get(uri);
345 exceptionOccurred(new SAXException("uri " + uri + " not handled"));
348 String str = h.endExternalXML(localName);
351 stack.peek().getStringBuilder().append(str);
355 if (converters != null) {
356 for (DocbookTagConverter conv : converters) {
357 conv.endElement(uri, localName, qName);
362 /*public void comment(char[] ch, int start, int length) throws SAXException {
369 public void characters(char[] ch, int start, int length) throws SAXException {
370 int end = start + length;
372 if (isEscapable(stack.peek().getName(), stack.peek().getURI())) {
373 StringBuilder buf = stack.peek().getStringBuilder();
375 for (int i = start; i < end; i++) {
378 buf.append(ch, save, i - save);
379 buf.append("'");
383 buf.append(ch, save, i - save);
384 buf.append(""");
388 buf.append(ch, save, i - save);
393 buf.append(ch, save, i - save);
398 buf.append(ch, save, i - save);
408 buf.append(ch, save, end - save);
411 stack.peek().getStringBuilder().append(ch, start, length);
414 if (converters != null) {
415 for (DocbookTagConverter conv : converters) {
416 conv.characters(ch, start, length);
424 public void setDocumentLocator(Locator locator) {
425 this.locator = locator;
429 * @return the document locator
431 public Locator getDocumentLocator() {
436 * @return the used stack
438 protected Stack<DocbookElement> getStack() {
443 * Handle an exception depending on the presence of a locator
444 * @param e the exception to handle
445 * @throws SAXException if problem
447 protected void fatalExceptionOccurred(Exception e) throws SAXException {
448 throw new SAXException(errors + "\nFATAL error:\n" + e.getMessage());
452 * Handle an exception depending on the presence of a locator
453 * @param e the exception to handle
454 * @throws SAXException if problem
456 protected void exceptionOccurred(Exception e) {
460 errors += makeErrorMessage(e);
463 private String makeErrorMessage(Exception e) {
465 if (currentFileName != null) {
466 sId = "SystemID:" + currentFileName;
471 file = in.getCanonicalPath();
472 } catch (IOException e1) {
473 e1.printStackTrace();
476 if (locator != null) {
477 return "\nCannot parse " + file + ":\n" + e.getMessage() + "\n" + sId + " at line " + locator.getLineNumber();
479 return "\nCannot parse " + file + ":\n" + e.getMessage() + "\n" + sId;
484 * @param the StringBuilder to trim
485 * @return a trimed StringBuilder
487 private static StringBuilder trim(StringBuilder buf) {
490 int end = buf.length();
492 for (; i < end; i++) {
494 if (c != ' ' && c != '\t' && c != '\r' && c != '\n') {
500 for (i = end - 1; i >= 0; i--) {
502 if (c != ' ' && c != '\t' && c != '\r' && c != '\n') {
506 buf.setLength(i + 1);