PipelineFactory.java 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724
  1. /* PipelineFactory.java --
  2. Copyright (C) 1999,2000,2001 Free Software Foundation, Inc.
  3. This file is part of GNU Classpath.
  4. GNU Classpath is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.
  8. GNU Classpath is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with GNU Classpath; see the file COPYING. If not, write to the
  14. Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  15. 02110-1301 USA.
  16. Linking this library statically or dynamically with other modules is
  17. making a combined work based on this library. Thus, the terms and
  18. conditions of the GNU General Public License cover the whole
  19. combination.
  20. As a special exception, the copyright holders of this library give you
  21. permission to link this library with independent modules to produce an
  22. executable, regardless of the license terms of these independent
  23. modules, and to copy and distribute the resulting executable under
  24. terms of your choice, provided that you also meet, for each linked
  25. independent module, the terms and conditions of the license of that
  26. module. An independent module is a module which is not derived from
  27. or based on this library. If you modify this library, you may extend
  28. this exception to your version of the library, but you are not
  29. obligated to do so. If you do not wish to do so, delete this
  30. exception statement from your version. */
  31. package gnu.xml.pipeline;
  32. import java.io.File;
  33. import java.io.FileOutputStream;
  34. import java.io.IOException;
  35. import java.io.OutputStream;
  36. import java.io.OutputStreamWriter;
  37. import java.lang.reflect.Constructor;
  38. import java.util.StringTokenizer;
  39. import org.xml.sax.*;
  40. import org.xml.sax.ext.*;
  41. /**
  42. * This provides static factory methods for creating simple event pipelines.
  43. * These pipelines are specified by strings, suitable for passing on
  44. * command lines or embedding in element attributes. For example, one way
  45. * to write a pipeline that restores namespace syntax, validates (stopping
  46. * the pipeline on validity errors) and then writes valid data to standard
  47. * output is this: <pre>
  48. * nsfix | validate | write ( stdout )</pre>
  49. *
  50. * <p> In this syntax, the tokens are always separated by whitespace, and each
  51. * stage of the pipeline may optionally have a parameter (which can be a
  52. * pipeline) in parentheses. Interior stages are called filters, and the
  53. * rightmost end of a pipeline is called a terminus.
  54. *
  55. * <p> Stages are usually implemented by a single class, which may not be
  56. * able to act as both a filter and a terminus; but any terminus can be
  57. * automatically turned into a filter, through use of a {@link TeeConsumer}.
  58. * The stage identifiers are either class names, or are one of the following
  59. * short identifiers built into this class. (Most of these identifiers are
  60. * no more than aliases for classes.) The built-in identifiers include:</p>
  61. <table border="1" cellpadding="3" cellspacing="0">
  62. <tr bgcolor="#ccccff" class="TableHeadingColor">
  63. <th align="center" width="5%">Stage</th>
  64. <th align="center" width="9%">Parameter</th>
  65. <th align="center" width="1%">Terminus</th>
  66. <th align="center">Description</th>
  67. </tr>
  68. <tr valign="top" align="center">
  69. <td><a href="../dom/Consumer.html">dom</a></td>
  70. <td><em>none</em></td>
  71. <td> yes </td>
  72. <td align="left"> Applications code can access a DOM Document built
  73. from the input event stream. When used as a filter, this buffers
  74. data up to an <em>endDocument</em> call, and then uses a DOM parser
  75. to report everything that has been recorded (which can easily be
  76. less than what was reported to it). </td>
  77. </tr>
  78. <tr valign="top" align="center">
  79. <td><a href="NSFilter.html">nsfix</a></td>
  80. <td><em>none</em></td>
  81. <td>no</td>
  82. <td align="left">This stage ensures that the XML element and attribute
  83. names in its output use namespace prefixes and declarations correctly.
  84. That is, so that they match the "Namespace plus LocalName" naming data
  85. with which each XML element and attribute is already associated. </td>
  86. </tr>
  87. <tr valign="top" align="center">
  88. <td><a href="EventFilter.html">null</a></td>
  89. <td><em>none</em></td>
  90. <td>yes</td>
  91. <td align="left">This stage ignores all input event data.</td>
  92. </tr>
  93. <tr valign="top" align="center">
  94. <td><a href="CallFilter.html">server</a></td>
  95. <td><em>required</em><br> server URL </td>
  96. <td>no</td>
  97. <td align="left">Sends its input as XML request to a remote server,
  98. normally a web application server using the HTTP or HTTPS protocols.
  99. The output of this stage is the parsed response from that server.</td>
  100. </tr>
  101. <tr valign="top" align="center">
  102. <td><a href="TeeConsumer.html">tee</a></td>
  103. <td><em>required</em><br> first pipeline</td>
  104. <td>no</td>
  105. <td align="left">This sends its events down two paths; its parameter
  106. is a pipeline descriptor for the first path, and the second path
  107. is the output of this stage.</td>
  108. </tr>
  109. <tr valign="top" align="center">
  110. <td><a href="ValidationConsumer.html">validate</a></td>
  111. <td><em>none</em></td>
  112. <td>yes</td>
  113. <td align="left">This checks for validity errors, and reports them
  114. through its error handler. The input must include declaration events
  115. and some lexical events. </td>
  116. </tr>
  117. <tr valign="top" align="center">
  118. <td><a href="WellFormednessFilter.html">wf</a></td>
  119. <td><em>none</em></td>
  120. <td>yes</td>
  121. <td align="left"> This class provides some basic "well formedness"
  122. tests on the input event stream, and reports a fatal error if any
  123. of them fail. One example: start/end calls for elements must match.
  124. No SAX parser is permitted to produce malformed output, but other
  125. components can easily do so.</td>
  126. </tr>
  127. <tr valign="top" align="center">
  128. <td>write</td>
  129. <td><em>required</em><br> "stdout", "stderr", or filename</td>
  130. <td>yes</td>
  131. <td align="left"> Writes its input to the specified output, as pretty
  132. printed XML text encoded using UTF-8. Input events must be well
  133. formed and "namespace fixed", else the output won't be XML (or possibly
  134. namespace) conformant. The symbolic names represent
  135. <em>System.out</em> and <em>System.err</em> respectively; names must
  136. correspond to files which don't yet exist.</td>
  137. </tr>
  138. <tr valign="top" align="center">
  139. <td>xhtml</td>
  140. <td><em>required</em><br> "stdout", "stderr", or filename</td>
  141. <td>yes</td>
  142. <td align="left"> Like <em>write</em> (above), except that XHTML rules
  143. are followed. The XHTML 1.0 Transitional document type is declared,
  144. and only ASCII characters are written (for interoperability). Other
  145. characters are written as entity or character references; the text is
  146. pretty printed.</td>
  147. </tr>
  148. <tr valign="top" align="center">
  149. <td><a href="XIncludeFilter.html">xinclude</a></td>
  150. <td><em>none</em></td>
  151. <td>no</td>
  152. <td align="left">This stage handles XInclude processing.
  153. This is like entity inclusion, except that the included content
  154. is declared in-line rather than in the DTD at the beginning of
  155. a document.
  156. </td>
  157. </tr>
  158. <tr valign="top" align="center">
  159. <td><a href="XsltFilter.html">xslt</a></td>
  160. <td><em>required</em><br> XSLT stylesheet URI</td>
  161. <td>no</td>
  162. <td align="left">This stage handles XSLT transformation
  163. according to a stylesheet.
  164. The implementation of the transformation may not actually
  165. stream data, although if such an XSLT engine is in use
  166. then that can happen.
  167. </td>
  168. </tr>
  169. </table>
  170. * <p> Note that {@link EventFilter#bind} can automatically eliminate
  171. * some filters by setting SAX2 parser features appropriately. This means
  172. * that you can routinely put filters like "nsfix", "validate", or "wf" at the
  173. * front of a pipeline (for components that need inputs conditioned to match
  174. * that level of correctness), and know that it won't actually be used unless
  175. * it's absolutely necessary.
  176. *
  177. * @author David Brownell
  178. */
  179. public class PipelineFactory
  180. {
  181. /**
  182. * Creates a simple pipeline according to the description string passed in.
  183. */
  184. public static EventConsumer createPipeline (String description)
  185. throws IOException
  186. {
  187. return createPipeline (description, null);
  188. }
  189. /**
  190. * Extends an existing pipeline by prepending the filter pipeline to the
  191. * specified consumer. Some pipelines need more customization than can
  192. * be done through this simplified syntax. When they are set up with
  193. * direct API calls, use this method to merge more complex pipeline
  194. * segments with easily configured ones.
  195. */
  196. public static EventConsumer createPipeline (
  197. String description,
  198. EventConsumer next
  199. ) throws IOException
  200. {
  201. // tokens are (for now) what's separated by whitespace;
  202. // very easy to parse, but IDs never have spaces.
  203. StringTokenizer tokenizer;
  204. String tokens [];
  205. tokenizer = new StringTokenizer (description);
  206. tokens = new String [tokenizer.countTokens ()];
  207. for (int i = 0; i < tokens.length; i++)
  208. tokens [i] = tokenizer.nextToken ();
  209. PipelineFactory factory = new PipelineFactory ();
  210. Pipeline pipeline = factory.parsePipeline (tokens, next);
  211. return pipeline.createPipeline ();
  212. }
  213. private PipelineFactory () { /* NYET */ }
  214. /**
  215. * Extends an existing pipeline by prepending a pre-tokenized filter
  216. * pipeline to the specified consumer. Tokens are class names (or the
  217. * predefined aliases) left and right parenthesis, and the vertical bar.
  218. */
  219. public static EventConsumer createPipeline (
  220. String tokens [],
  221. EventConsumer next
  222. ) throws IOException
  223. {
  224. PipelineFactory factory = new PipelineFactory ();
  225. Pipeline pipeline = factory.parsePipeline (tokens, next);
  226. return pipeline.createPipeline ();
  227. }
  228. private String tokens [];
  229. private int index;
  230. private Pipeline parsePipeline (String toks [], EventConsumer next)
  231. {
  232. tokens = toks;
  233. index = 0;
  234. Pipeline retval = parsePipeline (next);
  235. if (index != toks.length)
  236. throw new ArrayIndexOutOfBoundsException (
  237. "extra token: " + tokens [index]);
  238. return retval;
  239. }
  240. // pipeline ::= stage | stage '|' pipeline
  241. private Pipeline parsePipeline (EventConsumer next)
  242. {
  243. Pipeline retval = new Pipeline (parseStage ());
  244. // minimal pipelines: "stage" and "... | id"
  245. if (index > (tokens.length - 2)
  246. || !"|".equals (tokens [index])
  247. ) {
  248. retval.next = next;
  249. return retval;
  250. }
  251. index++;
  252. retval.rest = parsePipeline (next);
  253. return retval;
  254. }
  255. // stage ::= id | id '(' pipeline ')'
  256. private Stage parseStage ()
  257. {
  258. Stage retval = new Stage (tokens [index++]);
  259. // minimal stages: "id" and "id ( id )"
  260. if (index > (tokens.length - 2)
  261. || !"(".equals (tokens [index]) /*)*/
  262. )
  263. return retval;
  264. index++;
  265. retval.param = parsePipeline (null);
  266. if (index >= tokens.length)
  267. throw new ArrayIndexOutOfBoundsException (
  268. "missing right paren");
  269. if (/*(*/ !")".equals (tokens [index++]))
  270. throw new ArrayIndexOutOfBoundsException (
  271. "required right paren, not: " + tokens [index - 1]);
  272. return retval;
  273. }
  274. //
  275. // these classes obey the conventions for constructors, so they're
  276. // only built in to this table of shortnames
  277. //
  278. // - filter (one or two types of arglist)
  279. // * last constructor is 'next' element
  280. // * optional (first) string parameter
  281. //
  282. // - terminus (one or types of arglist)
  283. // * optional (only) string parameter
  284. //
  285. // terminus stages are transformed into filters if needed, by
  286. // creating a "tee". filter stages aren't turned to terminus
  287. // stages though; either eliminate such stages, or add some
  288. // terminus explicitly.
  289. //
  290. private static final String builtinStages [][] = {
  291. { "dom", "gnu.xml.dom.Consumer" },
  292. { "nsfix", "gnu.xml.pipeline.NSFilter" },
  293. { "null", "gnu.xml.pipeline.EventFilter" },
  294. { "server", "gnu.xml.pipeline.CallFilter" },
  295. { "tee", "gnu.xml.pipeline.TeeConsumer" },
  296. { "validate", "gnu.xml.pipeline.ValidationConsumer" },
  297. { "wf", "gnu.xml.pipeline.WellFormednessFilter" },
  298. { "xinclude", "gnu.xml.pipeline.XIncludeFilter" },
  299. { "xslt", "gnu.xml.pipeline.XsltFilter" },
  300. // XXX want: option for validate, to preload external part of a DTD
  301. // xhtml, write ... nyet generic-ready
  302. };
  303. private static class Stage
  304. {
  305. String id;
  306. Pipeline param;
  307. Stage (String name)
  308. { id = name; }
  309. public String toString ()
  310. {
  311. if (param == null)
  312. return id;
  313. return id + " ( " + param + " )";
  314. }
  315. private void fail (String message)
  316. throws IOException
  317. {
  318. throw new IOException ("in '" + id
  319. + "' stage of pipeline, " + message);
  320. }
  321. EventConsumer createStage (EventConsumer next)
  322. throws IOException
  323. {
  324. String name = id;
  325. // most builtins are just class aliases
  326. for (int i = 0; i < builtinStages.length; i++) {
  327. if (id.equals (builtinStages [i][0])) {
  328. name = builtinStages [i][1];
  329. break;
  330. }
  331. }
  332. // Save output as XML or XHTML text
  333. if ("write".equals (name) || "xhtml".equals (name)) {
  334. String filename;
  335. boolean isXhtml = "xhtml".equals (name);
  336. OutputStream out = null;
  337. TextConsumer consumer;
  338. if (param == null)
  339. fail ("parameter is required");
  340. filename = param.toString ();
  341. if ("stdout".equals (filename))
  342. out = System.out;
  343. else if ("stderr".equals (filename))
  344. out = System.err;
  345. else {
  346. File f = new File (filename);
  347. /*
  348. if (!f.isAbsolute ())
  349. fail ("require absolute file paths");
  350. */
  351. if (f.exists ())
  352. fail ("file already exists: " + f.getName ());
  353. // XXX this races against the existence test
  354. out = new FileOutputStream (f);
  355. }
  356. if (!isXhtml)
  357. consumer = new TextConsumer (out);
  358. else
  359. consumer = new TextConsumer (
  360. new OutputStreamWriter (out, "8859_1"),
  361. true);
  362. consumer.setPrettyPrinting (true);
  363. if (next == null)
  364. return consumer;
  365. return new TeeConsumer (consumer, next);
  366. } else {
  367. //
  368. // Here go all the builtins that are just aliases for
  369. // classes, and all stage IDs that started out as such
  370. // class names. The following logic relies on several
  371. // documented conventions for constructor invocation.
  372. //
  373. String msg = null;
  374. try {
  375. Class klass = Class.forName (name);
  376. Class argTypes [] = null;
  377. Constructor constructor = null;
  378. boolean filter = false;
  379. Object params [] = null;
  380. Object obj = null;
  381. // do we need a filter stage?
  382. if (next != null) {
  383. // "next" consumer is always passed, with
  384. // or without the optional string param
  385. if (param == null) {
  386. argTypes = new Class [1];
  387. argTypes [0] = EventConsumer.class;
  388. params = new Object [1];
  389. params [0] = next;
  390. msg = "no-param filter";
  391. } else {
  392. argTypes = new Class [2];
  393. argTypes [0] = String.class;
  394. argTypes [1] = EventConsumer.class;
  395. params = new Object [2];
  396. params [0] = param.toString ();
  397. params [1] = next;
  398. msg = "one-param filter";
  399. }
  400. try {
  401. constructor = klass.getConstructor (argTypes);
  402. } catch (NoSuchMethodException e) {
  403. // try creating a filter from a
  404. // terminus and a tee
  405. filter = true;
  406. msg += " built from ";
  407. }
  408. }
  409. // build from a terminus stage, with or
  410. // without the optional string param
  411. if (constructor == null) {
  412. String tmp;
  413. if (param == null) {
  414. argTypes = new Class [0];
  415. params = new Object [0];
  416. tmp = "no-param terminus";
  417. } else {
  418. argTypes = new Class [1];
  419. argTypes [0] = String.class;
  420. params = new Object [1];
  421. params [0] = param.toString ();
  422. tmp = "one-param terminus";
  423. }
  424. if (msg == null)
  425. msg = tmp;
  426. else
  427. msg += tmp;
  428. constructor = klass.getConstructor (argTypes);
  429. // NOT creating terminus by dead-ending
  430. // filters ... users should think about
  431. // that one, something's likely wrong
  432. }
  433. obj = constructor.newInstance (params);
  434. // return EventConsumers directly, perhaps after
  435. // turning them into a filter
  436. if (obj instanceof EventConsumer) {
  437. if (filter)
  438. return new TeeConsumer ((EventConsumer) obj, next);
  439. return (EventConsumer) obj;
  440. }
  441. // if it's not a handler, it's an error
  442. // we can wrap handlers in a filter
  443. EventFilter retval = new EventFilter ();
  444. boolean updated = false;
  445. if (obj instanceof ContentHandler) {
  446. retval.setContentHandler ((ContentHandler) obj);
  447. updated = true;
  448. }
  449. if (obj instanceof DTDHandler) {
  450. retval.setDTDHandler ((DTDHandler) obj);
  451. updated = true;
  452. }
  453. if (obj instanceof LexicalHandler) {
  454. retval.setProperty (
  455. EventFilter.PROPERTY_URI + "lexical-handler",
  456. obj);
  457. updated = true;
  458. }
  459. if (obj instanceof DeclHandler) {
  460. retval.setProperty (
  461. EventFilter.PROPERTY_URI + "declaration-handler",
  462. obj);
  463. updated = true;
  464. }
  465. if (!updated)
  466. fail ("class is neither Consumer nor Handler");
  467. if (filter)
  468. return new TeeConsumer (retval, next);
  469. return retval;
  470. } catch (IOException e) {
  471. throw e;
  472. } catch (NoSuchMethodException e) {
  473. fail (name + " constructor missing -- " + msg);
  474. } catch (ClassNotFoundException e) {
  475. fail (name + " class not found");
  476. } catch (Exception e) {
  477. // e.printStackTrace ();
  478. fail ("stage not available: " + e.getMessage ());
  479. }
  480. }
  481. // NOTREACHED
  482. return null;
  483. }
  484. }
  485. private static class Pipeline
  486. {
  487. Stage stage;
  488. // rest may be null
  489. Pipeline rest;
  490. EventConsumer next;
  491. Pipeline (Stage s)
  492. { stage = s; }
  493. public String toString ()
  494. {
  495. if (rest == null && next == null)
  496. return stage.toString ();
  497. if (rest != null)
  498. return stage + " | " + rest;
  499. throw new IllegalArgumentException ("next");
  500. }
  501. EventConsumer createPipeline ()
  502. throws IOException
  503. {
  504. if (next == null) {
  505. if (rest == null)
  506. next = stage.createStage (null);
  507. else
  508. next = stage.createStage (rest.createPipeline ());
  509. }
  510. return next;
  511. }
  512. }
  513. /*
  514. public static void main (String argv [])
  515. {
  516. try {
  517. // three basic terminus cases
  518. createPipeline ("null");
  519. createPipeline ("validate");
  520. createPipeline ("write ( stdout )");
  521. // four basic filters
  522. createPipeline ("nsfix | write ( stderr )");
  523. createPipeline ("wf | null");
  524. createPipeline ("null | null");
  525. createPipeline (
  526. "call ( http://www.example.com/services/xml-1a ) | xhtml ( stdout )");
  527. // tee junctions
  528. createPipeline ("tee ( validate ) | write ( stdout )");
  529. createPipeline ("tee ( nsfix | write ( stdout ) ) | validate");
  530. // longer pipeline
  531. createPipeline ("nsfix | tee ( validate ) | write ( stdout )");
  532. createPipeline (
  533. "null | wf | nsfix | tee ( validate ) | write ( stdout )");
  534. // try some parsing error cases
  535. try {
  536. createPipeline ("null ("); // extra token '('
  537. System.err.println ("** didn't report error");
  538. } catch (Exception e) {
  539. System.err.println ("== err: " + e.getMessage ()); }
  540. try {
  541. createPipeline ("nsfix |"); // extra token '|'
  542. System.err.println ("** didn't report error");
  543. } catch (Exception e) {
  544. System.err.println ("== err: " + e.getMessage ()); }
  545. try {
  546. createPipeline ("xhtml ( foo"); // missing right paren
  547. System.err.println ("** didn't report error");
  548. } catch (Exception e) {
  549. System.err.println ("== err: " + e.getMessage ()); }
  550. try {
  551. createPipeline ("xhtml ( foo bar"); // required right paren
  552. System.err.println ("** didn't report error");
  553. } catch (Exception e) {
  554. System.err.println ("== err: " + e.getMessage ()); }
  555. try {
  556. createPipeline ("tee ( nsfix | validate");// missing right paren
  557. System.err.println ("** didn't report error");
  558. } catch (Exception e) {
  559. System.err.println ("== err: " + e.getMessage ()); }
  560. // try some construction error cases
  561. try {
  562. createPipeline ("call"); // missing param
  563. System.err.println ("** didn't report error");
  564. } catch (Exception e) {
  565. System.err.println ("== err: " + e.getMessage ()); }
  566. try {
  567. createPipeline ("call ( foobar )"); // broken param
  568. System.err.println ("** didn't report error");
  569. } catch (Exception e) {
  570. System.err.println ("== err: " + e.getMessage ()); }
  571. try {
  572. createPipeline ("nsfix ( foobar )"); // illegal param
  573. System.err.println ("** didn't report error");
  574. } catch (Exception e) {
  575. System.err.println ("== err: " + e.getMessage ()); }
  576. try {
  577. createPipeline ("null ( foobar )"); // illegal param
  578. System.err.println ("** didn't report error");
  579. } catch (Exception e) {
  580. System.err.println ("== err: " + e.getMessage ()); }
  581. try {
  582. createPipeline ("wf ( foobar )"); // illegal param
  583. System.err.println ("** didn't report error");
  584. } catch (Exception e) {
  585. System.err.println ("== err: " + e.getMessage ()); }
  586. try {
  587. createPipeline ("xhtml ( foobar.html )");
  588. new File ("foobar.html").delete ();
  589. // now supported
  590. } catch (Exception e) {
  591. System.err.println ("** err: " + e.getMessage ()); }
  592. try {
  593. createPipeline ("xhtml"); // missing param
  594. System.err.println ("** didn't report error");
  595. } catch (Exception e) {
  596. System.err.println ("== err: " + e.getMessage ()); }
  597. try {
  598. createPipeline ("write ( stdout ) | null"); // nonterminal
  599. System.err.println ("** didn't report error");
  600. } catch (Exception e) {
  601. System.err.println ("== err: " + e.getMessage ()); }
  602. try {
  603. createPipeline ("validate | null");
  604. // now supported
  605. } catch (Exception e) {
  606. System.err.println ("** err: " + e.getMessage ()); }
  607. try {
  608. createPipeline ("validate ( foo )"); // illegal param
  609. System.err.println ("** didn't report error");
  610. } catch (Exception e) {
  611. System.err.println ("== err: " + e.getMessage ()); }
  612. try {
  613. createPipeline ("tee"); // missing param
  614. System.err.println ("** didn't report error");
  615. } catch (Exception e) {
  616. System.err.println ("== err: " + e.getMessage ()); }
  617. try {
  618. // only builtins so far
  619. createPipeline ("com.example.xml.FilterClass");
  620. System.err.println ("** didn't report error");
  621. } catch (Exception e) {
  622. System.err.println ("== err: " + e.getMessage ()); }
  623. } catch (Exception e) {
  624. e.printStackTrace ();
  625. }
  626. }
  627. /**/
  628. }