View Javadoc
1   package org.apache.maven.doxia.module.xdoc;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.FileFilter;
24  import java.io.FileReader;
25  import java.io.Reader;
26  import java.io.Writer;
27  
28  import java.util.Iterator;
29  import java.util.regex.Pattern;
30  
31  import org.apache.maven.doxia.parser.AbstractParserTest;
32  import org.apache.maven.doxia.parser.ParseException;
33  import org.apache.maven.doxia.parser.Parser;
34  import org.apache.maven.doxia.sink.Sink;
35  import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
36  import org.apache.maven.doxia.sink.impl.SinkEventElement;
37  import org.apache.maven.doxia.sink.impl.SinkEventTestingSink;
38  import org.codehaus.plexus.util.IOUtil;
39  
40  /**
41   * @author <a href="mailto:jason@maven.org">Jason van Zyl</a>
42   * @author <a href="mailto:evenisse@codehaus.org">Emmanuel Venisse</a>
43   * @version $Id$
44   * @since 1.0
45   */
46  public class XdocParserTest
47      extends AbstractParserTest
48  {
49      private XdocParser parser;
50  
51      @Override
52      protected void setUp()
53          throws Exception
54      {
55          super.setUp();
56  
57          parser = (XdocParser) lookup( Parser.ROLE, "xdoc" );
58  
59          // AbstractXmlParser.CachedFileEntityResolver downloads DTD/XSD files in ${java.io.tmpdir}
60          // Be sure to delete them
61          String tmpDir = System.getProperty( "java.io.tmpdir" );
62  
63          final Pattern xsdFilePattern = Pattern.compile( "(xdoc\\-.*|xml)\\.xsd" );
64  
65          File[] xsdFiles = new File( tmpDir ).listFiles( new FileFilter()
66          {
67  
68              public boolean accept( File pathname )
69              {
70                  return pathname.isFile() && xsdFilePattern.matcher( pathname.getName() ).matches();
71              }
72          } );
73  
74          for ( File xsdFile : xsdFiles )
75          {
76              xsdFile.delete();
77          }
78  
79          /* FileUtils 3.0.10 is about 5-8 times slower than File.listFiles() + regexp */
80  //        String includes = "xdoc-*.xsd, xml.xsd";
81  //        List<File> tmpFiles = FileUtils.getFiles( new File( tmpDir ), includes, null, true );
82  //        for ( File tmpFile  : tmpFiles )
83  //        {
84  //            tmpFile.delete();
85  //        }
86      }
87  
88      /** {@inheritDoc} */
89      protected String outputExtension()
90      {
91          return "xml";
92      }
93  
94      /** {@inheritDoc} */
95      protected Parser createParser()
96      {
97          return parser;
98      }
99  
100     /** @throws Exception  */
101     public void testSnippetMacro()
102         throws Exception
103     {
104         Writer output = null;
105         Reader reader = null;
106 
107         try
108         {
109             output = getTestWriter( "macro" );
110             reader = getTestReader( "macro" );
111 
112             Sink sink = new XdocSink( output );
113             createParser().parse( reader, sink );
114             sink.close();
115         }
116         finally
117         {
118             IOUtil.close( output );
119             IOUtil.close( reader );
120         }
121 
122         File f = getTestFile( getBasedir(), outputBaseDir() + getOutputDir() + "macro.xml" );
123         assertTrue( "The file " + f.getAbsolutePath() + " was not created", f.exists() );
124 
125         String content;
126         try
127         {
128             reader = new FileReader( f );
129             content = IOUtil.toString( reader );
130         }
131         finally
132         {
133             IOUtil.close( reader );
134         }
135 
136         assertTrue( content.indexOf( "&lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;" ) != -1 );
137     }
138 
139     /** @throws Exception  */
140     public void testTocMacro()
141         throws Exception
142     {
143         Writer output = null;
144         Reader reader = null;
145 
146         try
147         {
148             output = getTestWriter( "toc" );
149             reader = getTestReader( "toc" );
150 
151             Sink sink = new XdocSink( output );
152             createParser().parse( reader, sink );
153             sink.close();
154         }
155         finally
156         {
157             IOUtil.close( output );
158             IOUtil.close( reader );
159         }
160 
161         File f = getTestFile( getBasedir(), outputBaseDir() + getOutputDir() + "toc.xml" );
162         assertTrue( "The file " + f.getAbsolutePath() + " was not created", f.exists() );
163 
164         String content;
165         try
166         {
167             reader = new FileReader( f );
168             content = IOUtil.toString( reader );
169         }
170         finally
171         {
172             IOUtil.close( reader );
173         }
174 
175         // No section, only subsection 1 and 2
176         assertTrue( content.indexOf( "<a href=\"#Section_11\">Section 11</a>" ) != -1 );
177         assertTrue( content.indexOf( "<a href=\"#Section_1211\">Section 1211</a>" ) == -1 );
178     }
179 
180     private Iterator<SinkEventElement> parseText( String text )
181         throws ParseException
182     {
183         SinkEventTestingSink sink = new SinkEventTestingSink();
184 
185         parser.parse( text, sink );
186 
187         return sink.getEventList().iterator();
188     }
189 
190     /** @throws Exception  */
191     public void testHeadEventsList()
192         throws Exception
193     {
194         String text = "<document>"
195                 + "<properties><title>title</title>"
196                 + "<!-- Test comment: DOXIA-312 -->"
197                 + "<author email=\"a@b.c\">John Doe</author></properties>"
198                 + "<head>"
199                 + "<meta name=\"security\" content=\"low\"/>"
200                 + "<base href=\"http://maven.apache.org/\"/>"
201                 + "</head>"
202                 + "<body></body></document>";
203 
204         Iterator<SinkEventElement> it = parseText( text );
205 
206         assertStartsWith( it, "head", "title", "text", "title_", "comment", "author", "text", "author_" );
207 
208         SinkEventElement unknown = it.next();
209         assertEquals( "unknown", unknown.getName() );
210         assertEquals( "meta", unknown.getArgs()[0] );
211 
212         unknown = it.next();
213         assertEquals( "unknown", unknown.getName() );
214         assertEquals( "base", unknown.getArgs()[0] );
215 
216         assertEquals( it, "head_", "body", "body_" );
217 
218         // DOXIA-359
219         text = "<document>"
220                 + "<properties><title>properties title</title></properties>"
221                 + "<head><title>head title</title></head>"
222                 + "<body></body></document>";
223 
224         it = parseText( text );
225 
226         assertStartsWith( it, "head", "title" );
227 
228         SinkEventElement title = it.next();
229         assertEquals( "text", title.getName() );
230         assertEquals( "properties title", title.getArgs()[0] );
231 
232         assertEquals( it, "title_", "head_", "body",  "body_" );
233     }
234 
235     /** @throws Exception  */
236     public void testDocumentBodyEventsList()
237         throws Exception
238     {
239         String text = "<document><body></body></document>";
240 
241         Iterator<SinkEventElement> it = parseText( text );
242 
243         assertEquals( it, "body", "body_" );
244     }
245 
246     /** @throws Exception  */
247     public void testSectionEventsList()
248         throws Exception
249     {
250         String text = "<section name=\"sec 1\"><subsection name=\"sub 1\"></subsection></section>";
251 
252         Iterator<SinkEventElement> it = parseText( text );
253 
254         assertEquals( it, "section1", "sectionTitle1", "text", "sectionTitle1_", "section2", "sectionTitle2", "text",
255                       "sectionTitle2_", "section2_", "section1_" );
256     }
257 
258     /** @throws Exception  */
259     public void testSectionAttributes()
260         throws Exception
261     {
262         // DOXIA-448
263         String text = "<section name=\"section name\" class=\"foo\" id=\"bar\"></section>";
264 
265         Iterator<SinkEventElement> it = parseText( text );
266 
267         assertStartsWith( it, "anchor", "anchor_" );
268 
269         SinkEventElement next = it.next();
270         assertEquals( "section1", next.getName() );
271         SinkEventAttributeSet set = (SinkEventAttributeSet) next.getArgs()[0];
272         assertEquals( 3, set.getAttributeCount() );
273         assertTrue( set.containsAttribute( "name", "section name" ) );
274         assertTrue( set.containsAttribute( "class", "foo" ) );
275         assertTrue( set.containsAttribute( "id", "bar" ) );
276 
277         next = it.next();
278         assertEquals( "sectionTitle1", next.getName() );
279         assertNull( (SinkEventAttributeSet) next.getArgs()[0] );
280 
281         assertEquals( it, "text", "sectionTitle1_", "section1_" );
282     }
283 
284     /** @throws Exception  */
285     public void testNestedSectionsEventsList()
286         throws Exception
287     {
288         // DOXIA-241
289         String text = "<section name=\"section\"><h6>h6</h6><subsection name=\"subsection\"></subsection></section>";
290 
291         Iterator<SinkEventElement> it = parseText( text );
292 
293         assertEquals( it, "section1", "sectionTitle1", "text", "sectionTitle1_", "section2", "section3", "section4",
294                       "section5", "sectionTitle5", "text", "sectionTitle5_", "section5_", "section4_", "section3_",
295                       "section2_", "section2", "sectionTitle2", "text", "sectionTitle2_", "section2_", "section1_" );
296     }
297 
298     /** @throws Exception  */
299     public void testSourceEventsList()
300         throws Exception
301     {
302         String text = "<source><a href=\"what.html\">what</a></source>";
303 
304         Iterator<SinkEventElement> it = parseText( text );
305 
306         assertEquals( it, "verbatim", "link", "text", "link_", "verbatim_" );
307 
308         text = "<source><![CDATA[<a href=\"what.html\">what</a>]]></source>";
309         it = parseText( text );
310 
311         assertEquals( it, "verbatim", "text", "verbatim_" );
312 
313         text = "<source><![CDATA[<source>what</source>]]></source>";
314         it = parseText( text );
315 
316         assertEquals( it, "verbatim", "text", "verbatim_" );
317     }
318 
319     /** @throws Exception  */
320     public void testSourceContainingDTD()
321         throws Exception
322     {
323         String text = "<source><![CDATA[" +
324                           "<!DOCTYPE web-app PUBLIC " +
325                           "\"-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN\"" +
326                           " \"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd\">" +
327                       "]]></source>";
328 
329         Iterator<SinkEventElement> it = parseText( text );
330 
331         assertEquals( it, "verbatim", "text", "verbatim_" );
332     }
333 
334     /** @throws Exception  */
335     public void testPreEOL()
336         throws Exception
337     {
338         // test EOLs within <source>: the sink MUST receive a text event for the EOL
339         String text = "<source><a href=\"what.html\">what</a>" + EOL
340                 + "<a href=\"what.html\">what</a></source>";
341 
342         Iterator<SinkEventElement> it = parseText( text );
343 
344         assertEquals( it, "verbatim", "link", "text", "link_", "text", "link", "text", "link_", "verbatim_" );
345     }
346 
347     /**
348      * Test section with ids.
349      *
350      * @throws java.lang.Exception if any.
351      */
352     public void testSectionIdAnchor()
353         throws Exception
354     {
355         String text = "<section name=\"test\" id=\"test-id\">This is a test."
356                 + "<subsection name=\"sub-test\" id=\"sub-id\">Sub-section</subsection></section>";
357 
358         Iterator<SinkEventElement> it = parseText( text );
359 
360         assertEquals( it.next(), "anchor", "test-id" );
361 
362         assertStartsWith( it, "anchor_", "section1", "sectionTitle1", "text", "sectionTitle1_", "text" );
363 
364         assertEquals( it.next(), "anchor", "sub-id" );
365 
366         assertEquals( it, "anchor_", "section2", "sectionTitle2", "text", "sectionTitle2_", "text", "section2_",
367                       "section1_" );
368     }
369 
370     /**
371      * Test script block.
372      *
373      * @throws java.lang.Exception if any.
374      */
375     public void testJavaScript()
376         throws Exception
377     {
378         String text = "<script type=\"text/javascript\"><![CDATA[alert(\"Hello!\");]]></script>";
379 
380         Iterator<SinkEventElement> it = parseText( text );
381         assertEquals( it, "unknown", "unknown", "unknown" );
382     }
383 
384     /**
385      * Test unknown tags.
386      *
387      * @throws java.lang.Exception if any.
388      */
389     public void testUnknown()
390         throws Exception
391     {
392         String text = "<applet><param name=\"name\" value=\"value\"/><unknown/></applet>";
393 
394         Iterator<SinkEventElement> it = parseText( text );
395         assertEquals( it, "unknown", "unknown", "unknown", "unknown", "unknown" );
396     }
397 
398     /**
399      * Test invalid macro tags.
400      */
401     public void testMacroExceptions()
402     {
403         SinkEventTestingSink sink = new SinkEventTestingSink();
404         assertParseException( sink, "<macro/>" );
405         assertParseException( sink, "<macro name=\"\"/>" );
406         assertParseException( sink, "<macro name=\"name\"><param name=\"\" value=\"value\"/></macro>" );
407         assertParseException( sink, "<macro name=\"name\"><param name=\"name\" value=\"\"/></macro>" );
408         assertParseException( sink, "<macro name=\"name\"><param value=\"value\"/></macro>" );
409         assertParseException( sink, "<macro name=\"name\"><param name=\"name\"/></macro>" );
410         assertParseException( sink, "<macro name=\"unknown\"></macro>" );
411     }
412 
413     private void assertParseException( Sink sink, String text )
414     {
415         try
416         {
417             parser.parse( text, sink );
418 
419             fail( "Should not be parseable: '" + text + "'" );
420         }
421         catch ( ParseException ex )
422         {
423             assertNotNull( ex );
424         }
425     }
426 
427     /** @throws Exception  */
428     public void testEntities()
429         throws Exception
430     {
431         final String text = "<!DOCTYPE test [<!ENTITY foo \"&#x159;\"><!ENTITY tritPos  \"&#x1d7ed;\">]>"
432                 + "<section name=\"&amp;&foo;&tritPos;\"><p>&amp;&foo;&tritPos;</p></section>";
433 
434         SinkEventTestingSink sink = new SinkEventTestingSink();
435 
436         parser.setValidate( false );
437         parser.parse( text, sink );
438 
439         Iterator<SinkEventElement> it = sink.getEventList().iterator();
440 
441         assertStartsWith( it, "section1", "sectionTitle1" );
442 
443         assertEquals( it.next(), "text", "&\u0159\uD835\uDFED" );
444 
445         assertStartsWith( it, "sectionTitle1_", "paragraph" );
446 
447         assertEquals( it.next(), "text", "&" );
448 
449         assertEquals( it.next(), "text", "\u0159" );
450 
451         assertEquals( it.next(), "text", "\uD835\uDFED" );
452 
453         assertEquals( it, "paragraph_", "section1_" );
454     }
455     
456     public void testStyleWithCData() throws Exception
457     {
458         // DOXIA-449
459         final String text = "<style type=\"text/css\">\n" + 
460         		"<![CDATA[\n" + 
461         		"h2 {\n" + 
462         		"font-size: 50px;\n" + 
463         		"}\n" + 
464         		"]]>\n" + 
465         		"</style>"; 
466         
467         SinkEventTestingSink sink = new SinkEventTestingSink();
468 
469         parser.setValidate( false );
470         parser.parse( text, sink );
471         
472         Iterator<SinkEventElement> it = sink.getEventList().iterator();
473         SinkEventElement styleElm = it.next(); 
474         assertEquals( "unknown", styleElm.getName() );
475         assertEquals( "style", styleElm.getArgs()[0] );
476         SinkEventElement cdataElm = it.next(); 
477         assertEquals( "unknown", cdataElm.getName() );
478         assertEquals( "CDATA", cdataElm.getArgs()[0] );
479         SinkEventElement styleElm_ = it.next(); 
480         assertEquals( "unknown", styleElm_.getName() );
481         assertEquals( "style", styleElm_.getArgs()[0] );
482         assertFalse( it.hasNext() );
483     }
484 }