You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

FOTreeBuilder.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. /*
  2. * Copyright 1999-2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /* $Id$ */
  17. package org.apache.fop.fo;
  18. import java.io.BufferedReader;
  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.io.InputStreamReader;
  22. import java.io.OutputStream;
  23. import java.io.Reader;
  24. import java.util.ArrayList;
  25. import java.util.Enumeration;
  26. import java.util.Iterator;
  27. import java.util.List;
  28. import java.util.Map;
  29. import java.util.Set;
  30. import org.apache.commons.logging.Log;
  31. import org.apache.commons.logging.LogFactory;
  32. import org.apache.fop.apps.FOPException;
  33. import org.apache.fop.apps.FOUserAgent;
  34. import org.apache.fop.area.AreaTreeHandler;
  35. import org.apache.fop.render.mif.MIFHandler;
  36. import org.apache.fop.render.rtf.RTFHandler;
  37. import org.apache.fop.fo.ElementMapping.Maker;
  38. import org.apache.fop.fo.pagination.Root;
  39. import org.xml.sax.Attributes;
  40. import org.xml.sax.Locator;
  41. import org.xml.sax.SAXException;
  42. import org.xml.sax.helpers.DefaultHandler;
  43. /**
  44. * SAX Handler that passes parsed data to the various
  45. * FO objects, where they can be used either to build
  46. * an FO Tree, or used by Structure Renderers to build
  47. * other data structures.
  48. */
  49. public class FOTreeBuilder extends DefaultHandler {
  50. /**
  51. * Table mapping element names to the makers of objects
  52. * representing formatting objects.
  53. */
  54. protected Map fobjTable = new java.util.HashMap();
  55. /**
  56. * logging instance
  57. */
  58. protected Log log = LogFactory.getLog(FOTreeBuilder.class);
  59. /**
  60. * Set of mapped namespaces.
  61. */
  62. protected Set namespaces = new java.util.HashSet();
  63. /**
  64. * The root of the formatting object tree
  65. */
  66. protected Root rootFObj = null;
  67. /**
  68. * Current formatting object being handled
  69. */
  70. protected FONode currentFObj = null;
  71. /**
  72. * The class that handles formatting and rendering to a stream
  73. * (mark-fop@inomial.com)
  74. */
  75. private FOInputHandler foInputHandler;
  76. /** The SAX locator object managing the line and column counters */
  77. private Locator locator;
  78. /**
  79. * FOTreeBuilder constructor
  80. * @param render type as defined in Constants class
  81. * @param foUserAgent in effect for this process
  82. * @param stream OutputStream to direct results
  83. */
  84. public FOTreeBuilder(int renderType, FOUserAgent foUserAgent,
  85. OutputStream stream) throws FOPException {
  86. if (renderType == Constants.RENDER_MIF) {
  87. foInputHandler = new MIFHandler(foUserAgent, stream);
  88. } else if (renderType == Constants.RENDER_RTF) {
  89. foInputHandler = new RTFHandler(foUserAgent, stream);
  90. } else {
  91. if (renderType == Constants.NOT_SET) {
  92. throw new IllegalStateException(
  93. "Render must be set using setRender(int renderType)");
  94. }
  95. foInputHandler = new AreaTreeHandler(foUserAgent, renderType,
  96. stream);
  97. }
  98. // Add standard element mappings
  99. setupDefaultMappings();
  100. // add additional ElementMappings defined within FOUserAgent
  101. ArrayList addlEMs = foUserAgent.getAdditionalElementMappings();
  102. if (addlEMs != null) {
  103. for (int i = 0; i < addlEMs.size(); i++) {
  104. addElementMapping((String) addlEMs.get(i));
  105. }
  106. }
  107. }
  108. /**
  109. * Sets all the element and property list mappings to their default values.
  110. *
  111. */
  112. private void setupDefaultMappings() {
  113. addElementMapping("org.apache.fop.fo.FOElementMapping");
  114. addElementMapping("org.apache.fop.fo.extensions.svg.SVGElementMapping");
  115. addElementMapping("org.apache.fop.fo.extensions.svg.BatikExtensionElementMapping");
  116. addElementMapping("org.apache.fop.fo.extensions.ExtensionElementMapping");
  117. // add mappings from available services
  118. Iterator providers =
  119. Service.providers(ElementMapping.class);
  120. if (providers != null) {
  121. while (providers.hasNext()) {
  122. String str = (String)providers.next();
  123. try {
  124. addElementMapping(str);
  125. } catch (IllegalArgumentException e) {
  126. log.warn("Error while adding element mapping", e);
  127. }
  128. }
  129. }
  130. }
  131. /**
  132. * Add the element mapping with the given class name.
  133. * @param mappingClassName the class name representing the element mapping.
  134. * @throws IllegalArgumentException if there was not such element mapping.
  135. */
  136. public void addElementMapping(String mappingClassName)
  137. throws IllegalArgumentException {
  138. try {
  139. ElementMapping mapping =
  140. (ElementMapping)Class.forName(mappingClassName).newInstance();
  141. addElementMapping(mapping);
  142. } catch (ClassNotFoundException e) {
  143. throw new IllegalArgumentException("Could not find "
  144. + mappingClassName);
  145. } catch (InstantiationException e) {
  146. throw new IllegalArgumentException("Could not instantiate "
  147. + mappingClassName);
  148. } catch (IllegalAccessException e) {
  149. throw new IllegalArgumentException("Could not access "
  150. + mappingClassName);
  151. } catch (ClassCastException e) {
  152. throw new IllegalArgumentException(mappingClassName
  153. + " is not an ElementMapping");
  154. }
  155. }
  156. private void addElementMapping(ElementMapping mapping) {
  157. this.fobjTable.put(mapping.getNamespaceURI(), mapping.getTable());
  158. this.namespaces.add(mapping.getNamespaceURI().intern());
  159. }
  160. /**
  161. * SAX Handler for locator
  162. * @see org.xml.sax.ContentHandler#setDocumentLocator(Locator)
  163. */
  164. public void setDocumentLocator(Locator locator) {
  165. this.locator = locator;
  166. }
  167. /**
  168. * SAX Handler for characters
  169. * @see org.xml.sax.ContentHandler#characters(char[], int, int)
  170. */
  171. public void characters(char[] data, int start, int length) {
  172. if (currentFObj != null) {
  173. currentFObj.addCharacters(data, start, start + length, locator);
  174. }
  175. }
  176. /**
  177. * SAX Handler for the start of the document
  178. * @see org.xml.sax.ContentHandler#startDocument()
  179. */
  180. public void startDocument() throws SAXException {
  181. rootFObj = null; // allows FOTreeBuilder to be reused
  182. if (log.isDebugEnabled()) {
  183. log.debug("Building formatting object tree");
  184. }
  185. foInputHandler.startDocument();
  186. }
  187. /**
  188. * SAX Handler for the end of the document
  189. * @see org.xml.sax.ContentHandler#endDocument()
  190. */
  191. public void endDocument() throws SAXException {
  192. rootFObj = null;
  193. currentFObj = null;
  194. if (log.isDebugEnabled()) {
  195. log.debug("Parsing of document complete");
  196. }
  197. foInputHandler.endDocument();
  198. }
  199. /**
  200. * SAX Handler for the start of an element
  201. * @see org.xml.sax.ContentHandler#startElement(String, String, String, Attributes)
  202. */
  203. public void startElement(String namespaceURI, String localName, String rawName,
  204. Attributes attlist) throws SAXException {
  205. /* the node found in the FO document */
  206. FONode foNode;
  207. // Check to ensure first node encountered is an fo:root
  208. if (rootFObj == null) {
  209. if (!namespaceURI.equals(FOElementMapping.URI)
  210. || !localName.equals("root")) {
  211. throw new SAXException(new IllegalArgumentException(
  212. "Error: First element must be fo:root formatting object"));
  213. }
  214. } else { // check that incoming node is valid for currentFObj
  215. try {
  216. currentFObj.validateChildNode(locator, namespaceURI, localName);
  217. } catch (IllegalArgumentException e) {
  218. throw new SAXException(e);
  219. }
  220. }
  221. ElementMapping.Maker fobjMaker = findFOMaker(namespaceURI, localName);
  222. // System.out.println("found a " + fobjMaker.toString());
  223. try {
  224. foNode = fobjMaker.make(currentFObj);
  225. foNode.processNode(localName, locator, attlist);
  226. } catch (IllegalArgumentException e) {
  227. throw new SAXException(e);
  228. } catch (FOPException e) {
  229. throw new SAXException(e);
  230. }
  231. if (rootFObj == null) {
  232. rootFObj = (Root) foNode;
  233. rootFObj.setFOInputHandler(foInputHandler);
  234. } else {
  235. currentFObj.addChild(foNode);
  236. }
  237. currentFObj = foNode;
  238. }
  239. /**
  240. * SAX Handler for the end of an element
  241. * @see org.xml.sax.ContentHandler#endElement(String, String, String)
  242. */
  243. public void endElement(String uri, String localName, String rawName)
  244. throws SAXException {
  245. try {
  246. currentFObj.end();
  247. } catch (IllegalArgumentException e) {
  248. throw new SAXException(e);
  249. }
  250. currentFObj = currentFObj.getParent();
  251. }
  252. /**
  253. * Finds the Maker used to create FO objects of a particular type
  254. * @param namespaceURI URI for the namespace of the element
  255. * @param localName name of the Element
  256. * @return the ElementMapping.Maker that can create an FO object for this element
  257. */
  258. private Maker findFOMaker(String namespaceURI, String localName) {
  259. Map table = (Map)fobjTable.get(namespaceURI);
  260. Maker fobjMaker = null;
  261. if (table != null) {
  262. fobjMaker = (ElementMapping.Maker)table.get(localName);
  263. // try default
  264. if (fobjMaker == null) {
  265. fobjMaker = (ElementMapping.Maker)table.get(ElementMapping.DEFAULT);
  266. }
  267. }
  268. if (fobjMaker == null) {
  269. if (log.isWarnEnabled()) {
  270. log.warn("Unknown formatting object " + namespaceURI + "^" + localName);
  271. }
  272. if (namespaces.contains(namespaceURI.intern())) {
  273. // fall back
  274. fobjMaker = new Unknown.Maker();
  275. } else {
  276. fobjMaker = new UnknownXMLObj.Maker(namespaceURI);
  277. }
  278. }
  279. return fobjMaker;
  280. }
  281. /**
  282. * Resets this object for another run.
  283. */
  284. public void reset() {
  285. currentFObj = null;
  286. rootFObj = null;
  287. foInputHandler = null;
  288. }
  289. }
  290. // code stolen from org.apache.batik.util and modified slightly
  291. // does what sun.misc.Service probably does, but it cannot be relied on.
  292. // hopefully will be part of standard jdk sometime.
  293. /**
  294. * This class loads services present in the class path.
  295. */
  296. class Service {
  297. private static Map providerMap = new java.util.Hashtable();
  298. public static synchronized Iterator providers(Class cls) {
  299. ClassLoader cl = cls.getClassLoader();
  300. // null if loaded by bootstrap class loader
  301. if (cl == null) {
  302. cl = ClassLoader.getSystemClassLoader();
  303. }
  304. String serviceFile = "META-INF/services/" + cls.getName();
  305. // log.debug("File: " + serviceFile);
  306. List lst = (List)providerMap.get(serviceFile);
  307. if (lst != null) {
  308. return lst.iterator();
  309. }
  310. lst = new java.util.Vector();
  311. providerMap.put(serviceFile, lst);
  312. Enumeration e;
  313. try {
  314. e = cl.getResources(serviceFile);
  315. } catch (IOException ioe) {
  316. return lst.iterator();
  317. }
  318. while (e.hasMoreElements()) {
  319. try {
  320. java.net.URL u = (java.net.URL)e.nextElement();
  321. //log.debug("URL: " + u);
  322. InputStream is = u.openStream();
  323. Reader r = new InputStreamReader(is, "UTF-8");
  324. BufferedReader br = new BufferedReader(r);
  325. String line = br.readLine();
  326. while (line != null) {
  327. try {
  328. // First strip any comment...
  329. int idx = line.indexOf('#');
  330. if (idx != -1) {
  331. line = line.substring(0, idx);
  332. }
  333. // Trim whitespace.
  334. line = line.trim();
  335. // If nothing left then loop around...
  336. if (line.length() == 0) {
  337. line = br.readLine();
  338. continue;
  339. }
  340. // log.debug("Line: " + line);
  341. // Try and load the class
  342. // Object obj = cl.loadClass(line).newInstance();
  343. // stick it into our vector...
  344. lst.add(line);
  345. } catch (Exception ex) {
  346. // Just try the next line
  347. }
  348. line = br.readLine();
  349. }
  350. } catch (Exception ex) {
  351. // Just try the next file...
  352. }
  353. }
  354. return lst.iterator();
  355. }
  356. }