maven-doxia-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From vsive...@apache.org
Subject svn commit: r560737 [1/2] - in /maven/doxia/doxia/trunk/doxia-modules: doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/ doxia-module-xdoc/src/test/java/org/apache/maven/doxia/module/xdoc/ doxia-module-xhtml/src/main/java/org/apache/m...
Date Sun, 29 Jul 2007 15:40:14 GMT
Author: vsiveton
Date: Sun Jul 29 08:40:13 2007
New Revision: 560737

URL: http://svn.apache.org/viewvc?view=rev&rev=560737
Log:
o Put all hardcoded markups in an interface
o using javax.swing.text.html.HTML.Tag and javax.swing.text.html.HTML.Attribute instead of harcoded tags/attributes
o updated javadocs

Added:
    maven/doxia/doxia/trunk/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocMarkup.java   (with props)
    maven/doxia/doxia/trunk/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/XhtmlMarkup.java   (with props)
Modified:
    maven/doxia/doxia/trunk/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocParser.java
    maven/doxia/doxia/trunk/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocSink.java
    maven/doxia/doxia/trunk/doxia-modules/doxia-module-xdoc/src/test/java/org/apache/maven/doxia/module/xdoc/XdocParserTest.java
    maven/doxia/doxia/trunk/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/XhtmlParser.java
    maven/doxia/doxia/trunk/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/XhtmlSink.java

Added: maven/doxia/doxia/trunk/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocMarkup.java
URL: http://svn.apache.org/viewvc/maven/doxia/doxia/trunk/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocMarkup.java?view=auto&rev=560737
==============================================================================
--- maven/doxia/doxia/trunk/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocMarkup.java (added)
+++ maven/doxia/doxia/trunk/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocMarkup.java Sun Jul 29 08:40:13 2007
@@ -0,0 +1,147 @@
+package org.apache.maven.doxia.module.xdoc;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import javax.swing.text.html.HTML.Tag;
+
+/**
+ * List of <code>Xdoc</code> markups.
+ * <br/>
+ * Xdoc uses several  {@link javax.swing.text.html.HTML.Tag} and {@link javax.swing.text.html.HTML.Attribute}
+ * as markups and custom tags.
+ *
+ * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
+ * @version $Id$
+ * @since 1.0
+ */
+public interface XdocMarkup
+{
+    /** The vm line separator */
+    /** TODO should be in a super interface */
+    String EOL = System.getProperty( "line.separator" );
+
+    // ----------------------------------------------------------------------
+    // Markup separators
+    // ----------------------------------------------------------------------
+
+    /** TODO should be in a super XML interface */
+
+    /** Xhtml start markup: '<' */
+    String START_MARKUP = "<";
+
+    /** Xhtml end markup: '>' */
+    String END_MARKUP = ">";
+
+    /** Xhtml quote markup: '\"' */
+    String QUOTE_MARKUP = "\"";
+
+    /** Xhtml slash markup: '/' */
+    String SLASH_MARKUP = "/";
+
+    /** Xhtml space markup: ' ' */
+    String SPACE_MARKUP = " ";
+
+    /** Xhtml equal markup: '=' */
+    String EQUAL_MARKUP = "=";
+
+    // ----------------------------------------------------------------------
+    // Specific Xdoc tags
+    // ----------------------------------------------------------------------
+
+    /** Xdoc tag for <code>author</code> */
+    Tag AUTHOR_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "author";
+        }
+    };
+
+    /** Xdoc tag for <code>date</code> */
+    Tag DATE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "date";
+        }
+    };
+
+    /** Xdoc tag for <code>document</code> */
+    Tag DOCUMENT_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "document";
+        }
+    };
+
+    /** Xdoc tag for <code>macro</code> */
+    Tag MACRO_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "macro";
+        }
+    };
+
+    /** Xdoc tag for <code>properties</code> */
+    Tag PROPERTIES_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "properties";
+        }
+    };
+
+    /** Xdoc tag for <code>section</code> */
+    Tag SECTION_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "section";
+        }
+    };
+
+    /** Xdoc tag for <code>source</code> */
+    Tag SOURCE_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "source";
+        }
+    };
+
+    /** Xdoc tag for <code>subsection</code> */
+    Tag SUBSECTION_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "subsection";
+        }
+    };
+}

Propchange: maven/doxia/doxia/trunk/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocMarkup.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/doxia/doxia/trunk/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocMarkup.java
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Modified: maven/doxia/doxia/trunk/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocParser.java
URL: http://svn.apache.org/viewvc/maven/doxia/doxia/trunk/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocParser.java?view=diff&rev=560737&r1=560736&r2=560737
==============================================================================
--- maven/doxia/doxia/trunk/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocParser.java (original)
+++ maven/doxia/doxia/trunk/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocParser.java Sun Jul 29 08:40:13 2007
@@ -26,6 +26,9 @@
 import java.util.HashMap;
 import java.util.Map;
 
+import javax.swing.text.html.HTML.Attribute;
+import javax.swing.text.html.HTML.Tag;
+
 import org.apache.maven.doxia.macro.MacroExecutionException;
 import org.apache.maven.doxia.macro.manager.MacroNotFoundException;
 import org.apache.maven.doxia.macro.MacroRequest;
@@ -42,11 +45,13 @@
  * Parse an xdoc model and emit events into the specified doxia Sink.
  *
  * @author <a href="mailto:jason@maven.org">Jason van Zyl</a>
- * @version $Id:XdocParser.java 348605 2005-11-24 12:02:44 +1100 (Thu, 24 Nov 2005) brett $
+ * @version $Id$
+ * @since 1.0
  * @plexus.component role="org.apache.maven.doxia.parser.Parser" role-hint="xdoc"
  */
 public class XdocParser
     extends AbstractParser
+    implements XdocMarkup
 {
     /** The source content of the input reader. Used to pass into macros. */
     private String sourceContent;
@@ -146,82 +151,82 @@
     {
         isEmptyElement = parser.isEmptyElementTag();
 
-        if ( parser.getName().equals( "document" ) )
+        if ( parser.getName().equals( DOCUMENT_TAG.toString() ) )
         {
             //Do nothing
             return;
         }
-        else if ( parser.getName().equals( "title" ) )
+        else if ( parser.getName().equals( Tag.TITLE.toString() ) )
         {
             sink.title();
         }
-        else if ( parser.getName().equals( "author" ) )
+        else if ( parser.getName().equals( AUTHOR_TAG.toString() ) )
         {
             sink.author();
         }
-        else if ( parser.getName().equals( "body" ) )
+        else if ( parser.getName().equals( Tag.BODY.toString() ) )
         {
             sink.body();
         }
-        else if ( parser.getName().equals( "section" ) )
+        else if ( parser.getName().equals( SECTION_TAG.toString() ) )
         {
             // TODO this should go into a sink? kept for compat for the moment
-            sink.anchor( parser.getAttributeValue( null, "name" ) );
+            sink.anchor( parser.getAttributeValue( null, Attribute.NAME.toString() ) );
             sink.anchor_();
 
             sink.section1();
 
             sink.sectionTitle1();
 
-            sink.text( parser.getAttributeValue( null, "name" ) );
+            sink.text( parser.getAttributeValue( null, Attribute.NAME.toString() ) );
 
             sink.sectionTitle1_();
         }
-        else if ( parser.getName().equals( "subsection" ) )
+        else if ( parser.getName().equals( SUBSECTION_TAG.toString() ) )
         {
             // TODO this should go into a sink? kept for compat for the moment
-            sink.anchor( parser.getAttributeValue( null, "name" ) );
+            sink.anchor( parser.getAttributeValue( null, Attribute.NAME.toString() ) );
             sink.anchor_();
 
             sink.section2();
 
             sink.sectionTitle2();
 
-            sink.text( parser.getAttributeValue( null, "name" ) );
+            sink.text( parser.getAttributeValue( null, Attribute.NAME.toString() ) );
 
             sink.sectionTitle2_();
         }
         // TODO section3 section4 section5
-        else if ( parser.getName().equals( "h4" ) )
+        else if ( parser.getName().equals( Tag.H4.toString() ) )
         {
             sink.sectionTitle3();
         }
-        else if ( parser.getName().equals( "h5" ) )
+        else if ( parser.getName().equals( Tag.H5.toString() ) )
         {
             sink.sectionTitle4();
         }
-        else if ( parser.getName().equals( "h6" ) )
+        else if ( parser.getName().equals( Tag.H6.toString() ) )
         {
             sink.sectionTitle5();
         }
-        else if ( parser.getName().equals( "p" ) )
+        else if ( parser.getName().equals( Tag.P.toString() ) )
         {
             sink.paragraph();
         }
-        else if ( parser.getName().equals( "source" ) )
+        else if ( parser.getName().equals( SOURCE_TAG.toString() ) )
         {
             sink.verbatim( true );
         }
-        else if ( parser.getName().equals( "ul" ) )
+        else if ( parser.getName().equals( Tag.UL.toString() ) )
         {
             sink.list();
         }
-        else if ( parser.getName().equals( "ol" ) )
+        else if ( parser.getName().equals( Tag.OL.toString() ) )
         {
             sink.numberedList( Sink.NUMBERING_DECIMAL );
             orderedListDepth++;
         }
-        else if ( parser.getName().equals( "li" ) )
+        else if ( parser.getName().equals( Tag.LI.toString() ) )
         {
             if ( orderedListDepth == 0 )
             {
@@ -232,38 +237,38 @@
                 sink.numberedListItem();
             }
         }
-        else if ( parser.getName().equals( "dl" ) )
+        else if ( parser.getName().equals( Tag.DL.toString() ) )
         {
             sink.definitionList();
         }
-        else if ( parser.getName().equals( "dt" ) )
+        else if ( parser.getName().equals( Tag.DT.toString() ) )
         {
             sink.definitionListItem();
             sink.definedTerm();
         }
-        else if ( parser.getName().equals( "dd" ) )
+        else if ( parser.getName().equals( Tag.DD.toString() ) )
         {
             sink.definition();
         }
-        else if ( parser.getName().equals( "properties" ) )
+        else if ( parser.getName().equals( PROPERTIES_TAG.toString() ) )
         {
             sink.head();
         }
-        else if ( parser.getName().equals( "b" ) )
+        else if ( parser.getName().equals( Tag.B.toString() ) )
         {
             sink.bold();
         }
-        else if ( parser.getName().equals( "i" ) )
+        else if ( parser.getName().equals( Tag.I.toString() ) )
         {
             sink.italic();
         }
-        else if ( parser.getName().equals( "tt" ) )
+        else if ( parser.getName().equals( Tag.TT.toString() ) )
         {
             sink.monospaced();
         }
-        else if ( parser.getName().equals( "a" ) )
+        else if ( parser.getName().equals( Tag.A.toString() ) )
         {
-            String href = parser.getAttributeValue( null, "href" );
+            String href = parser.getAttributeValue( null, Attribute.HREF.toString() );
             if ( href != null )
             {
                 sink.link( href );
@@ -271,7 +276,7 @@
             }
             else
             {
-                String name = parser.getAttributeValue( null, "name" );
+                String name = parser.getAttributeValue( null, Attribute.NAME.toString() );
                 if ( name != null )
                 {
                     sink.anchor( name );
@@ -283,11 +288,11 @@
                 }
             }
         }
-        else if ( parser.getName().equals( "macro" ) )
+        else if ( parser.getName().equals( MACRO_TAG.toString() ) )
         {
             if ( !secondParsing )
             {
-                String macroName = parser.getAttributeValue( null, "name" );
+                String macroName = parser.getAttributeValue( null, Attribute.NAME.toString() );
 
                 int count = parser.getAttributeCount();
 
@@ -298,6 +303,7 @@
                     parameters.put( parser.getAttributeName( i ), parser.getAttributeValue( i ) );
                 }
 
+                // TODO handles specific attributes
                 parameters.put( "sourceContent", sourceContent );
 
                 XdocParser xdocParser = new XdocParser();
@@ -321,17 +327,17 @@
         // Tables
         // ----------------------------------------------------------------------
 
-        else if ( parser.getName().equals( "table" ) )
+        else if ( parser.getName().equals( Tag.TABLE.toString() ) )
         {
             sink.table();
         }
-        else if ( parser.getName().equals( "tr" ) )
+        else if ( parser.getName().equals( Tag.TR.toString() ) )
         {
             sink.tableRow();
         }
-        else if ( parser.getName().equals( "th" ) )
+        else if ( parser.getName().equals( Tag.TH.toString() ) )
         {
-            String colspan = parser.getAttributeValue( null, "colspan" );
+            String colspan = parser.getAttributeValue( null, Attribute.COLSPAN.toString() );
             if ( colspan ==  null )
             {
                 sink.tableHeaderCell();
@@ -341,9 +347,9 @@
                 sink.tableHeaderCell( colspan );
             }
         }
-        else if ( parser.getName().equals( "td" ) )
+        else if ( parser.getName().equals( Tag.TD.toString() ) )
         {
-            String colspan = parser.getAttributeValue( null, "colspan" );
+            String colspan = parser.getAttributeValue( null, Attribute.COLSPAN.toString() );
             if ( colspan ==  null )
             {
                 sink.tableCell();
@@ -358,18 +364,18 @@
         // Empty elements: <br/>, <hr/> and <img />
         // ----------------------------------------------------------------------
 
-        else if ( parser.getName().equals( "br" ) )
+        else if ( parser.getName().equals( Tag.BR.toString() ) )
         {
             sink.lineBreak();
         }
-        else if ( parser.getName().equals( "hr" ) )
+        else if ( parser.getName().equals( Tag.HR.toString() ) )
         {
             sink.horizontalRule();
         }
-        else if ( parser.getName().equals( "img" ) )
+        else if ( parser.getName().equals( Tag.IMG.toString() ) )
         {
-            String src = parser.getAttributeValue( null, "src" );
-            String alt = parser.getAttributeValue( null, "alt" );
+            String src = parser.getAttributeValue( null, Attribute.SRC.toString() );
+            String alt = parser.getAttributeValue( null, Attribute.ALT.toString() );
 
             sink.figure();
             sink.figureGraphics( src );
@@ -398,41 +404,41 @@
      */
     private void handleEndTag( XmlPullParser parser, Sink sink )
     {
-        if ( parser.getName().equals( "document" ) )
+        if ( parser.getName().equals( DOCUMENT_TAG.toString() ) )
         {
             //Do nothing
             return;
         }
-        else if ( parser.getName().equals( "title" ) )
+        else if ( parser.getName().equals( Tag.TITLE.toString() ) )
         {
             sink.title_();
         }
-        else if ( parser.getName().equals( "author" ) )
+        else if ( parser.getName().equals( AUTHOR_TAG.toString() ) )
         {
             sink.author_();
         }
-        else if ( parser.getName().equals( "body" ) )
+        else if ( parser.getName().equals( Tag.BODY.toString() ) )
         {
             sink.body_();
         }
-        else if ( parser.getName().equals( "p" ) )
+        else if ( parser.getName().equals( Tag.P.toString() ) )
         {
             sink.paragraph_();
         }
-        else if ( parser.getName().equals( "source" ) )
+        else if ( parser.getName().equals( SOURCE_TAG.toString() ) )
         {
             sink.verbatim_();
         }
-        else if ( parser.getName().equals( "ul" ) )
+        else if ( parser.getName().equals( Tag.UL.toString() ) )
         {
             sink.list_();
         }
-        else if ( parser.getName().equals( "ol" ) )
+        else if ( parser.getName().equals( Tag.OL.toString() ) )
         {
             sink.numberedList_();
             orderedListDepth--;
         }
-        else if ( parser.getName().equals( "li" ) )
+        else if ( parser.getName().equals( Tag.LI.toString() ) )
         {
             if ( orderedListDepth == 0 )
             {
@@ -443,36 +449,36 @@
                 sink.numberedListItem_();
             }
         }
-        else if ( parser.getName().equals( "dl" ) )
+        else if ( parser.getName().equals( Tag.DL.toString() ) )
         {
             sink.definitionList_();
         }
-        else if ( parser.getName().equals( "dt" ) )
+        else if ( parser.getName().equals( Tag.DT.toString() ) )
         {
             sink.definedTerm_();
         }
-        else if ( parser.getName().equals( "dd" ) )
+        else if ( parser.getName().equals( Tag.DD.toString() ) )
         {
             sink.definition_();
             sink.definitionListItem_();
         }
-        else if ( parser.getName().equals( "properties" ) )
+        else if ( parser.getName().equals( PROPERTIES_TAG.toString() ) )
         {
             sink.head_();
         }
-        else if ( parser.getName().equals( "b" ) )
+        else if ( parser.getName().equals( Tag.B.toString() ) )
         {
             sink.bold_();
         }
-        else if ( parser.getName().equals( "i" ) )
+        else if ( parser.getName().equals( Tag.I.toString() ) )
         {
             sink.italic_();
         }
-        else if ( parser.getName().equals( "tt" ) )
+        else if ( parser.getName().equals( Tag.TT.toString() ) )
         {
             sink.monospaced_();
         }
-        else if ( parser.getName().equals( "a" ) )
+        else if ( parser.getName().equals( Tag.A.toString() ) )
         {
             if ( isLink )
             {
@@ -485,54 +491,54 @@
                 isAnchor = false;
             }
         }
-        else if ( parser.getName().equals( "macro" ) )
+        else if ( parser.getName().equals( MACRO_TAG.toString() ) )
         {
             //Do nothing
             return;
         }
-        else if ( parser.getName().equals( "table" ) )
+        else if ( parser.getName().equals( Tag.TABLE.toString() ) )
         {
             sink.table_();
         }
-        else if ( parser.getName().equals( "tr" ) )
+        else if ( parser.getName().equals( Tag.TR.toString() ) )
         {
             sink.tableRow_();
         }
-        else if ( parser.getName().equals( "th" ) )
+        else if ( parser.getName().equals( Tag.TH.toString() ) )
         {
             sink.tableHeaderCell_();
         }
-        else if ( parser.getName().equals( "td" ) )
+        else if ( parser.getName().equals( Tag.TD.toString() ) )
         {
             sink.tableCell_();
         }
-        else if ( parser.getName().equals( "section" ) )
+        else if ( parser.getName().equals( SECTION_TAG.toString() ) )
         {
             sink.section1_();
         }
-        else if ( parser.getName().equals( "subsection" ) )
+        else if ( parser.getName().equals( SUBSECTION_TAG.toString() ) )
         {
             sink.section2_();
         }
-        else if ( parser.getName().equals( "h4" ) )
+        else if ( parser.getName().equals( Tag.H4.toString() ) )
         {
             sink.sectionTitle3_();
         }
-        else if ( parser.getName().equals( "h5" ) )
+        else if ( parser.getName().equals( Tag.H5.toString() ) )
         {
             sink.sectionTitle4_();
         }
-        else if ( parser.getName().equals( "h6" ) )
+        else if ( parser.getName().equals( Tag.H6.toString() ) )
         {
             sink.sectionTitle5_();
         }
         else if ( !isEmptyElement )
         {
-            sink.rawText( "</" );
+            sink.rawText( START_MARKUP + SLASH_MARKUP );
 
             sink.rawText( parser.getName() );
 
-            sink.rawText( ">" );
+            sink.rawText( END_MARKUP );
         }
         else
         {
@@ -564,7 +570,7 @@
      */
     private void handleRawText( Sink sink, XmlPullParser parser )
     {
-        sink.rawText( "<" );
+        sink.rawText( START_MARKUP );
 
         sink.rawText( parser.getName() );
 
@@ -572,19 +578,19 @@
 
         for ( int i = 0; i < count; i++ )
         {
-            sink.rawText( " " );
+            sink.rawText( SPACE_MARKUP );
 
             sink.rawText( parser.getAttributeName( i ) );
 
-            sink.rawText( "=" );
+            sink.rawText( EQUAL_MARKUP );
 
-            sink.rawText( "\"" );
+            sink.rawText( QUOTE_MARKUP );
 
             sink.rawText( parser.getAttributeValue( i ) );
 
-            sink.rawText( "\"" );
+            sink.rawText( QUOTE_MARKUP );
         }
 
-        sink.rawText( ">" );
+        sink.rawText( END_MARKUP );
     }
 }

Modified: maven/doxia/doxia/trunk/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocSink.java
URL: http://svn.apache.org/viewvc/maven/doxia/doxia/trunk/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocSink.java?view=diff&rev=560737&r1=560736&r2=560737
==============================================================================
--- maven/doxia/doxia/trunk/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocSink.java (original)
+++ maven/doxia/doxia/trunk/doxia-modules/doxia-module-xdoc/src/main/java/org/apache/maven/doxia/module/xdoc/XdocSink.java Sun Jul 29 08:40:13 2007
@@ -20,9 +20,16 @@
  */
 
 import java.io.Writer;
+import java.util.Enumeration;
+
+import javax.swing.text.AttributeSet;
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.html.HTML.Attribute;
+import javax.swing.text.html.HTML.Tag;
 
-import org.apache.maven.doxia.util.HtmlTools;
 import org.apache.maven.doxia.sink.SinkAdapter;
+import org.apache.maven.doxia.util.HtmlTools;
 import org.apache.maven.doxia.util.LineBreaker;
 import org.apache.maven.doxia.parser.Parser;
 
@@ -30,13 +37,16 @@
  * A doxia Sink which produces an xdoc model.
  *
  * @author <a href="mailto:james@jamestaylor.org">James Taylor</a>
- * @version $Id:XdocSink.java 348605 2005-11-24 12:02:44 +1100 (Thu, 24 Nov 2005) brett $
+ * @version $Id$
+ * @since 1.0
  */
 public class XdocSink
     extends SinkAdapter
+    implements XdocMarkup
 {
-    /** System-dependent EOL. */
-    protected static final String EOL = System.getProperty( "line.separator" );
+    // ----------------------------------------------------------------------
+    // Instance fields
+    // ----------------------------------------------------------------------
 
     /** The LineBreaker to write the result. */
     protected LineBreaker out;
@@ -69,6 +79,10 @@
     /** Number of cells in a table row. */
     private int cellCount;
 
+    // ----------------------------------------------------------------------
+    //
+    // ----------------------------------------------------------------------
+
     /**
      * Constructor, initialize the LineBreaker.
      *
@@ -79,6 +93,10 @@
         this.out = new LineBreaker( writer );
     }
 
+    // ----------------------------------------------------------------------
+    // Public protected methods
+    // ----------------------------------------------------------------------
+
     /**
      * Reset all variables.
      */
@@ -93,7 +111,11 @@
         cellCount = 0;
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see XdocMarkup#DOCUMENT_TAG
+     * @see XdocMarkup#PROPERTIES_TAG
+     */
     public void head()
     {
         resetState();
@@ -102,67 +124,87 @@
 
         markup( "<?xml version=\"1.0\" ?>" + EOL );
 
-        markup( "<document>" + EOL );
+        writeStartTag( DOCUMENT_TAG );
 
-        markup( "<properties>" + EOL );
+        writeStartTag( PROPERTIES_TAG );
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see XdocMarkup#DOCUMENT_TAG
+     * @see XdocMarkup#PROPERTIES_TAG
+     */
     public void head_()
     {
         headFlag = false;
 
-        markup( "</properties>" + EOL );
+        writeEndTag( PROPERTIES_TAG );
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#TITLE
+     */
     public void title_()
     {
         if ( buffer.length() > 0 )
         {
-            markup( "<title>" );
+            writeStartTag( Tag.TITLE );
             content( buffer.toString() );
-            markup( "</title>" + EOL );
+            writeEndTag( Tag.TITLE );
             buffer = new StringBuffer();
         }
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see XdocMarkup#AUTHOR_TAG
+     */
     public void author_()
     {
         if ( buffer.length() > 0 )
         {
-            markup( "<author>" );
+            writeStartTag( AUTHOR_TAG );
             content( buffer.toString() );
-            markup( "</author>" + EOL );
+            writeEndTag( AUTHOR_TAG );
             buffer = new StringBuffer();
         }
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see XdocMarkup#DATE_TAG
+     */
     public void date_()
     {
         if ( buffer.length() > 0 )
         {
-            markup( "<date>" );
+            writeStartTag( DATE_TAG );
             content( buffer.toString() );
-            markup( "</date>" );
+            writeEndTag( DATE_TAG );
             buffer = new StringBuffer();
         }
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#BODY
+     */
     public void body()
     {
-        markup( "<body>" + EOL );
+        writeStartTag( Tag.BODY );
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#BODY
+     * @see XdocMarkup#DOCUMENT_TAG
+     */
     public void body_()
     {
-        markup( "</body>" + EOL );
+        writeEndTag( Tag.BODY );
 
-        markup( "</document>" + EOL );
+        writeEndTag( DOCUMENT_TAG );
 
         out.flush();
 
@@ -297,16 +339,20 @@
      * Starts a section.
      *
      * @param depth The level of the section.
+     * @see XdocMarkup#SECTION_TAG
+     * @see XdocMarkup#SUBSECTION_TAG
      */
     private void onSection( int depth )
     {
         if ( depth == 1 )
         {
-            markup( "<section name=\"" );
+            markup( START_MARKUP + SECTION_TAG.toString() + SPACE_MARKUP + Attribute.NAME + EQUAL_MARKUP
+                + QUOTE_MARKUP );
         }
         else if ( depth == 2 )
         {
-            markup( "<subsection name=\"" );
+            markup( START_MARKUP + SUBSECTION_TAG.toString() + SPACE_MARKUP + Attribute.NAME + EQUAL_MARKUP
+                + QUOTE_MARKUP );
         }
     }
 
@@ -314,20 +360,23 @@
      * Starts a section title.
      *
      * @param depth The level of the section title.
+     * @see javax.swing.text.html.HTML.Tag#H4
+     * @see javax.swing.text.html.HTML.Tag#H4
+     * @see javax.swing.text.html.HTML.Tag#H6
      */
     private void onSectionTitle( int depth )
     {
         if ( depth == 3 )
         {
-            markup( "<h4>" );
+            writeStartTag( Tag.H4 );
         }
         else if ( depth == 4 )
         {
-            markup( "<h5>" );
+            writeStartTag( Tag.H5 );
         }
         else if ( depth == 5 )
         {
-            markup( "<h6>" );
+            writeStartTag( Tag.H6 );
         }
 
         titleFlag = true;
@@ -337,24 +386,27 @@
      * Ends a section title.
      *
      * @param depth The level of the section title.
+     * @see javax.swing.text.html.HTML.Tag#H4
+     * @see javax.swing.text.html.HTML.Tag#H4
+     * @see javax.swing.text.html.HTML.Tag#H6
      */
     private void onSectionTitle_( int depth )
     {
         if ( depth == 1 || depth == 2 )
         {
-            markup( "\">" );
+            markup( QUOTE_MARKUP + END_MARKUP );
         }
         else if ( depth == 3 )
         {
-            markup( "</h4>" );
+            writeEndTag( Tag.H4 );
         }
         else if ( depth == 4 )
         {
-            markup( "</h5>" );
+            writeEndTag( Tag.H5 );
         }
         else if ( depth == 5 )
         {
-            markup( "</h6>" );
+            writeEndTag( Tag.H6 );
         }
 
         titleFlag = false;
@@ -364,16 +416,18 @@
      * Ends a section.
      *
      * @param depth The level of the section.
+     * @see XdocMarkup#SECTION_TAG
+     * @see XdocMarkup#SUBSECTION_TAG
      */
     private void onSection_( int depth )
     {
         if ( depth == 1 )
         {
-            markup( "</section>" );
+            writeEndTag( SECTION_TAG );
         }
         else if ( depth == 2 )
         {
-            markup( "</subsection>" );
+            writeEndTag( SUBSECTION_TAG );
         }
     }
 
@@ -381,34 +435,51 @@
     //
     // -----------------------------------------------------------------------
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#UL
+     */
     public void list()
     {
-        markup( "<ul>" + EOL );
+        writeStartTag( Tag.UL );
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#UL
+     */
     public void list_()
     {
-        markup( "</ul>" );
+        writeEndTag( Tag.UL );
         itemFlag = false;
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#LI
+     */
     public void listItem()
     {
-        markup( "<li>" );
+        writeStartTag( Tag.LI );
         itemFlag = true;
         // What follows is at least a paragraph.
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#LI
+     */
     public void listItem_()
     {
-        markup( "</li>" + EOL );
+        writeEndTag( Tag.LI );
     }
 
-    /** {@inheritDoc} */
+    /**
+     * The default list style depends on the numbering.
+     *
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#OL
+     */
     public void numberedList( int numbering )
     {
         String style;
@@ -430,109 +501,149 @@
             default:
                 style = "decimal";
         }
-        markup( "<ol style=\"list-style-type: " + style + "\">" + EOL );
+
+        MutableAttributeSet att = new SimpleAttributeSet();
+        att.addAttribute( Attribute.STYLE, "list-style-type: " + style );
+
+        writeStartTag( Tag.OL, att );
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#OL
+     */
     public void numberedList_()
     {
-        markup( "</ol>" );
+        writeEndTag( Tag.OL );
         itemFlag = false;
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#LI
+     */
     public void numberedListItem()
     {
-        markup( "<li>" );
+        writeStartTag( Tag.LI );
         itemFlag = true;
         // What follows is at least a paragraph.
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#LI
+     */
     public void numberedListItem_()
     {
-        markup( "</li>" + EOL );
+        writeEndTag( Tag.LI );
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#DL
+     */
     public void definitionList()
     {
-        markup( "<dl>" + EOL );
+        writeStartTag( Tag.DL );
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#DL
+     */
     public void definitionList_()
     {
-        markup( "</dl>" );
+        writeEndTag( Tag.DL );
         itemFlag = false;
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#DT
+     */
     public void definedTerm()
     {
-        markup( "<dt>" );
+        writeStartTag( Tag.DT );
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#DT
+     */
     public void definedTerm_()
     {
-        markup( "</dt>" + EOL );
+        writeEndTag( Tag.DT );
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#DD
+     */
     public void definition()
     {
-        markup( "<dd>" );
+        writeStartTag( Tag.DD );
         itemFlag = true;
         // What follows is at least a paragraph.
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#DD
+     */
     public void definition_()
     {
-        markup( "</dd>" + EOL );
+        writeEndTag( Tag.DD );
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#IMG
+     */
     public void figure()
     {
-        markup( "<img" );
+        markup( START_MARKUP + Tag.IMG );
     }
 
     /** {@inheritDoc} */
     public void figure_()
     {
-        markup( " />" );
+        markup( SPACE_MARKUP + SLASH_MARKUP + END_MARKUP );
     }
 
     /** {@inheritDoc} */
     public void figureGraphics( String s )
     {
-        markup( " src=\"" + s + "\"" );
+        markup( SPACE_MARKUP + Attribute.SRC + EQUAL_MARKUP + QUOTE_MARKUP + s + QUOTE_MARKUP );
     }
 
     /** {@inheritDoc} */
     public void figureCaption()
     {
-        markup( " alt=\"" );
+        markup( SPACE_MARKUP + Attribute.ALT + EQUAL_MARKUP + QUOTE_MARKUP );
     }
 
     /** {@inheritDoc} */
     public void figureCaption_()
     {
-        markup( "\"" );
+        markup( QUOTE_MARKUP );
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#P
+     */
     public void paragraph()
     {
         if ( !itemFlag )
         {
-            markup( "<p>" );
+            writeStartTag( Tag.P );
         }
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#P
+     */
     public void paragraph_()
     {
         if ( itemFlag )
@@ -541,35 +652,43 @@
         }
         else
         {
-            markup( "</p>" );
+            writeEndTag( Tag.P );
         }
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see XdocMarkup#SOURCE_TAG
+     * @see javax.swing.text.html.HTML.Tag#PRE
+     */
     public void verbatim( boolean boxed )
     {
         verbatimFlag = true;
         boxedFlag = boxed;
         if ( boxed )
         {
-            markup( "<source>" );
+            writeStartTag( SOURCE_TAG );
         }
         else
         {
-            markup( "<pre>" );
+            writeStartTag( Tag.PRE );
         }
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see XdocMarkup#SOURCE_TAG
+     * @see javax.swing.text.html.HTML.Tag#PRE
+     */
     public void verbatim_()
     {
         if ( boxedFlag )
         {
-            markup( "</source>" );
+            writeEndTag( SOURCE_TAG );
         }
         else
         {
-            markup( "</pre>" );
+            writeEndTag( Tag.PRE );
         }
 
         verbatimFlag = false;
@@ -577,49 +696,84 @@
         boxedFlag = false;
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#HR
+     */
     public void horizontalRule()
     {
-        markup( "<hr />" );
+        writeSimpleTag( Tag.HR );
     }
 
-    /** {@inheritDoc} */
+    /**
+     * The default align is <code>center</code>.
+     *
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#TABLE
+     */
     public void table()
     {
-        markup( "<table align=\"center\">" + EOL );
+        MutableAttributeSet att = new SimpleAttributeSet();
+        att.addAttribute( Attribute.ALIGN, "center" );
+
+        writeStartTag( Tag.TABLE, att );
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#TABLE
+     */
     public void table_()
     {
-        markup( "</table>" );
+        writeEndTag( Tag.TABLE );
     }
 
-    /** {@inheritDoc} */
+    /**
+     * The default align is <code>center</code>.
+     *
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#TABLE
+     */
     public void tableRows( int[] justification, boolean grid )
-
     {
-        markup( "<table align=\"center\" border=\"" + ( grid ? 1 : 0 ) + "\">" + EOL );
-        this.cellJustif = justification;
+        MutableAttributeSet att = new SimpleAttributeSet();
+        att.addAttribute( Attribute.ALIGN, "center" );
+        att.addAttribute( Attribute.BORDER, ( grid ? "1" : "0" ) );
+
+        writeStartTag( Tag.TABLE, att );
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#TABLE
+     */
     public void tableRows_()
     {
-        markup( "</table>" );
+        writeEndTag( Tag.TABLE );
     }
 
-    /** {@inheritDoc} */
+    /**
+     * The default valign is <code>top</code>.
+     *
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#TR
+     */
     public void tableRow()
     {
-        markup( "<tr valign=\"top\">" + EOL );
+        MutableAttributeSet att = new SimpleAttributeSet();
+        att.addAttribute( Attribute.VALIGN, "top" );
+
+        writeStartTag( Tag.TR, att );
         cellCount = 0;
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#TR
+     */
     public void tableRow_()
     {
-        markup( "</tr>" + EOL );
+        writeEndTag( Tag.TR );
         cellCount = 0;
     }
 
@@ -638,7 +792,9 @@
     /**
      * Starts a table cell.
      *
-     * @param headerRow If this cell is part of a header row.
+     * @param headerRow true if it is an header row
+     * @see javax.swing.text.html.HTML.Tag#TH
+     * @see javax.swing.text.html.HTML.Tag#TD
      */
     public void tableCell( boolean headerRow )
     {
@@ -661,14 +817,17 @@
             }
         }
 
+        Tag t = ( headerRow ? Tag.TH : Tag.TD );
+
+        MutableAttributeSet att = null;
+
         if ( justif != null )
         {
-            markup( "<t" + ( headerRow ? 'h' : 'd' ) + " align=\"" + justif + "\">" );
-        }
-        else
-        {
-            markup( "<t" + ( headerRow ? 'h' : 'd' ) + ">" );
+            att = new SimpleAttributeSet();
+            att.addAttribute( Attribute.ALIGN, justif );
         }
+
+        writeStartTag( t, att );
     }
 
     /** {@inheritDoc} */
@@ -686,118 +845,172 @@
     /**
      * Ends a table cell.
      *
-     * @param headerRow If this cell is part of a header row.
+     * @param headerRow true if it is an header row
+     * @see javax.swing.text.html.HTML.Tag#TH
+     * @see javax.swing.text.html.HTML.Tag#TD
      */
     public void tableCell_( boolean headerRow )
     {
-        markup( "</t" + ( headerRow ? 'h' : 'd' ) + ">" + EOL );
+        Tag t = ( headerRow ? Tag.TH : Tag.TD );
+        writeEndTag( t );
         ++cellCount;
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#P
+     * @see javax.swing.text.html.HTML.Tag#I
+     */
     public void tableCaption()
     {
-        markup( "<p><i>" );
+        writeStartTag( Tag.P );
+        writeStartTag( Tag.I );
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#P
+     * @see javax.swing.text.html.HTML.Tag#I
+     */
     public void tableCaption_()
     {
-        markup( "</i></p>" );
+        writeEndTag( Tag.P );
+        writeEndTag( Tag.I );
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#A
+     */
     public void anchor( String name )
     {
         if ( !headFlag && !titleFlag )
         {
             String id = HtmlTools.encodeId( name );
-            markup( "<a id=\"" + id + "\" name=\"" + id + "\">" );
+
+            MutableAttributeSet att = new SimpleAttributeSet();
+            att.addAttribute( Attribute.ID, id );
+            att.addAttribute( Attribute.NAME, id );
+
+            writeStartTag( Tag.A, att );
         }
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#A
+     */
     public void anchor_()
     {
         if ( !headFlag && !titleFlag )
         {
-            markup( "</a>" );
+            writeEndTag( Tag.A );
         }
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#A
+     */
     public void link( String name )
     {
         if ( !headFlag && !titleFlag )
         {
-            markup( "<a href=\"" + name + "\">" );
+            MutableAttributeSet att = new SimpleAttributeSet();
+            att.addAttribute( Attribute.HREF, name );
+
+            writeStartTag( Tag.A, att );
         }
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#A
+     */
     public void link_()
     {
         if ( !headFlag && !titleFlag )
         {
-            markup( "</a>" );
+            writeEndTag( Tag.A );
         }
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#I
+     */
     public void italic()
     {
         if ( !headFlag && !titleFlag )
         {
-            markup( "<i>" );
+            writeStartTag( Tag.I );
         }
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#I
+     */
     public void italic_()
     {
         if ( !headFlag && !titleFlag )
         {
-            markup( "</i>" );
+            writeEndTag( Tag.I );
         }
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#B
+     */
     public void bold()
     {
         if ( !headFlag && !titleFlag )
         {
-            markup( "<b>" );
+            writeStartTag( Tag.B );
         }
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#B
+     */
     public void bold_()
     {
         if ( !headFlag && !titleFlag )
         {
-            markup( "</b>" );
+            writeEndTag( Tag.B );
         }
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#TT
+     */
     public void monospaced()
     {
         if ( !headFlag && !titleFlag )
         {
-            markup( "<tt>" );
+            writeStartTag( Tag.TT );
         }
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#TT
+     */
     public void monospaced_()
     {
         if ( !headFlag && !titleFlag )
         {
-            markup( "</tt>" );
+            writeEndTag( Tag.TT );
         }
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @see javax.swing.text.html.HTML.Tag#BR
+     */
     public void lineBreak()
     {
         if ( headFlag || titleFlag )
@@ -806,7 +1019,7 @@
         }
         else
         {
-            markup( "<br />" );
+            writeSimpleTag( Tag.BR );
         }
     }
 
@@ -879,7 +1092,7 @@
      *
      * @param text the String to escape, may be null
      * @return the text escaped, "" if null String input
-     * @see org.apache.maven.doxia.util.HtmlTools#escapeHTML(String).
+     * @see org.apache.maven.doxia.util.HtmlTools#escapeHTML(String)
      */
     public static String escapeHTML( String text )
     {
@@ -891,7 +1104,7 @@
      *
      * @param text the String to encode, may be null.
      * @return the text encoded, null if null String input.
-     * @see org.apache.maven.doxia.util.HtmlTools#encodeURL(String).
+     * @see org.apache.maven.doxia.util.HtmlTools#encodeURL(String)
      */
     public static String encodeURL( String text )
     {
@@ -908,5 +1121,146 @@
     public void close()
     {
         out.close();
+    }
+
+    // ----------------------------------------------------------------------
+    // TODO Move these in core utils
+    // ----------------------------------------------------------------------
+
+    /**
+     * Starts a Tag, for instance:
+     * <pre>
+     * &lt;tag&gt;
+     * </pre>
+     *
+     * @param t a non null tag
+     * @param att a set of attributes
+     * @see #writeStartTag(Tag, MutableAttributeSet)
+     */
+    private void writeStartTag ( Tag t )
+    {
+        writeStartTag ( t, null );
+    }
+
+    /**
+     * Starts a Tag with attributes, for instance:
+     * <pre>
+     * &lt;tag attName="attValue" &gt;
+     * </pre>
+     *
+     * @param t a non null tag
+     * @param att a set of attributes
+     * @see #writeStartTag(Tag, MutableAttributeSet, boolean)
+     */
+    private void writeStartTag ( Tag t, MutableAttributeSet att )
+    {
+        writeStartTag ( t, att, false );
+    }
+
+    /**
+     * Starts a Tag with attributes, for instance:
+     * <pre>
+     * &lt;tag attName="attValue" &gt;
+     * </pre>
+     *
+     * @param t a non null tag
+     * @param att a set of attributes
+     * @param isSimpleTag boolean to write as a simple tag
+     */
+    private void writeStartTag( Tag t, MutableAttributeSet att, boolean isSimpleTag )
+    {
+        if ( t == null )
+        {
+            throw new IllegalArgumentException( "A tag is required" );
+        }
+
+        StringBuffer sb = new StringBuffer();
+        sb.append( START_MARKUP );
+        sb.append( t.toString() );
+
+        if ( att != null )
+        {
+            Enumeration names = att.getAttributeNames();
+
+            while ( names.hasMoreElements() )
+            {
+                Object key = names.nextElement();
+                Object value = att.getAttribute( key );
+
+                if ( value instanceof AttributeSet )
+                {
+                    // ignored
+                }
+                else
+                {
+                    sb.append( SPACE_MARKUP ).append( key.toString() ).append( EQUAL_MARKUP ).append( QUOTE_MARKUP )
+                        .append( value.toString() ).append( QUOTE_MARKUP );
+                }
+            }
+        }
+
+        if ( isSimpleTag )
+        {
+            sb.append( SLASH_MARKUP );
+        }
+
+        sb.append( END_MARKUP );
+
+        if ( isSimpleTag )
+        {
+            sb.append( EOL );
+        }
+
+        markup( sb.toString() );
+    }
+
+    /**
+     * Ends a Tag, for instance:
+     * <pre>
+     * &lt;/tag&gt;
+     * </pre>
+     *
+     * @param t a tag
+     */
+    private void writeEndTag( Tag t )
+    {
+        StringBuffer sb = new StringBuffer();
+        sb.append( START_MARKUP );
+        sb.append( SLASH_MARKUP );
+        sb.append( t.toString() );
+        sb.append( END_MARKUP );
+
+        sb.append( EOL );
+
+        markup( sb.toString() );
+    }
+
+    /**
+     * Starts a simple Tag, for instance:
+     * <pre>
+     * &lt;tag /&gt;
+     * </pre>
+     *
+     * @param t a non null tag
+     * @see #writeSimpleTag(Tag, MutableAttributeSet)
+     */
+    private void writeSimpleTag( Tag t )
+    {
+        writeSimpleTag( t, null );
+    }
+
+    /**
+     * Starts a simple Tag with attributes, for instance:
+     * <pre>
+     * &lt;tag attName="attValue" /&gt;
+     * </pre>
+     *
+     * @param t a non null tag
+     * @param att a set of attributes
+     * @see #writeStartTag(Tag, MutableAttributeSet, boolean)
+     */
+    private void writeSimpleTag ( Tag t, MutableAttributeSet att )
+    {
+        writeStartTag ( t, att, true );
     }
 }

Modified: maven/doxia/doxia/trunk/doxia-modules/doxia-module-xdoc/src/test/java/org/apache/maven/doxia/module/xdoc/XdocParserTest.java
URL: http://svn.apache.org/viewvc/maven/doxia/doxia/trunk/doxia-modules/doxia-module-xdoc/src/test/java/org/apache/maven/doxia/module/xdoc/XdocParserTest.java?view=diff&rev=560737&r1=560736&r2=560737
==============================================================================
--- maven/doxia/doxia/trunk/doxia-modules/doxia-module-xdoc/src/test/java/org/apache/maven/doxia/module/xdoc/XdocParserTest.java (original)
+++ maven/doxia/doxia/trunk/doxia-modules/doxia-module-xdoc/src/test/java/org/apache/maven/doxia/module/xdoc/XdocParserTest.java Sun Jul 29 08:40:13 2007
@@ -105,8 +105,8 @@
             getParser().parse( reader, sink );
 
             // No section, only subsection 1 and 2
-            assertTrue( output.toString().indexOf( "<li><a href=\"#section_11\">Section 11</a></li>" ) != -1 );
-            assertTrue( output.toString().indexOf( "<li><a href=\"#section_1211\">Section 1211</a></li>" ) == -1 );
+            assertTrue( noNewLine( output.toString() ).indexOf( "<li><a href=\"#section_11\">Section 11</a></li>" ) != -1 );
+            assertTrue( noNewLine( output.toString() ).indexOf( "<li><a href=\"#section_1211\">Section 1211</a></li>" ) == -1 );
         }
         finally
         {
@@ -120,5 +120,17 @@
                 reader.close();
             }
         }
+    }
+
+    /**
+     * TODO move me!
+     *
+     * @param text
+     * @return
+     */
+    private String noNewLine( String text )
+    {
+        String EOL = System.getProperty( "line.separator" );
+        return text.replaceAll( EOL, "" );
     }
 }

Added: maven/doxia/doxia/trunk/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/XhtmlMarkup.java
URL: http://svn.apache.org/viewvc/maven/doxia/doxia/trunk/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/XhtmlMarkup.java?view=auto&rev=560737
==============================================================================
--- maven/doxia/doxia/trunk/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/XhtmlMarkup.java (added)
+++ maven/doxia/doxia/trunk/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/XhtmlMarkup.java Sun Jul 29 08:40:13 2007
@@ -0,0 +1,77 @@
+package org.apache.maven.doxia.module.xhtml;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import javax.swing.text.html.HTML.Tag;
+
+/**
+ * List of <code>Xhtml</code> markups.
+ * <br/>
+ * Xhtml uses all {@link javax.swing.text.html.HTML.Tag} and {@link javax.swing.text.html.HTML.Attribute}
+ * as markups.
+ *
+ * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
+ * @version $Id$
+ * @since 1.0
+ */
+public interface XhtmlMarkup
+{
+    /** The vm line separator */
+    /** TODO should be in a super interface */
+    String EOL = System.getProperty( "line.separator" );
+
+    // ----------------------------------------------------------------------
+    // Markup separators
+    // ----------------------------------------------------------------------
+
+    /** TODO should be in a super XML interface */
+
+    /** Xhtml start markup: '<' */
+    String START_MARKUP = "<";
+
+    /** Xhtml end markup: '>' */
+    String END_MARKUP = ">";
+
+    /** Xhtml quote markup: '\"' */
+    String QUOTE_MARKUP = "\"";
+
+    /** Xhtml slash markup: '/' */
+    String SLASH_MARKUP = "/";
+
+    /** Xhtml space markup: ' ' */
+    String SPACE_MARKUP = " ";
+
+    /** Xhtml equal markup: '=' */
+    String EQUAL_MARKUP = "=";
+
+    // ----------------------------------------------------------------------
+    // Specific XHTML tags
+    // ----------------------------------------------------------------------
+
+    /** Xhtml tag for <code>tbody</code> */
+    Tag TBODY_TAG = new Tag()
+    {
+        /** {@inheritDoc} */
+        public String toString()
+        {
+            return "tbody";
+        }
+    };
+}

Propchange: maven/doxia/doxia/trunk/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/XhtmlMarkup.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: maven/doxia/doxia/trunk/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/XhtmlMarkup.java
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Modified: maven/doxia/doxia/trunk/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/XhtmlParser.java
URL: http://svn.apache.org/viewvc/maven/doxia/doxia/trunk/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/XhtmlParser.java?view=diff&rev=560737&r1=560736&r2=560737
==============================================================================
--- maven/doxia/doxia/trunk/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/XhtmlParser.java (original)
+++ maven/doxia/doxia/trunk/doxia-modules/doxia-module-xhtml/src/main/java/org/apache/maven/doxia/module/xhtml/XhtmlParser.java Sun Jul 29 08:40:13 2007
@@ -19,24 +19,30 @@
  * under the License.
  */
 
+import java.io.IOException;
 import java.io.Reader;
 import java.util.Stack;
 
+import javax.swing.text.html.HTML.Attribute;
+import javax.swing.text.html.HTML.Tag;
+
 import org.apache.maven.doxia.parser.ParseException;
 import org.apache.maven.doxia.parser.Parser;
 import org.apache.maven.doxia.sink.Sink;
 import org.codehaus.plexus.util.xml.pull.MXParser;
 import org.codehaus.plexus.util.xml.pull.XmlPullParser;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
 
 /**
  * Parse an xdoc model and emit events into the specified doxia
  * Sink.
  *
  * @author <a href="mailto:jason@maven.org">Jason van Zyl</a>
- * @version $Id:XhtmlParser.java 348605 2005-11-24 12:02:44 +1100 (Thu, 24 Nov 2005) brett $
+ * @version $Id$
+ * @since 1.0
  */
 public class XhtmlParser
-    implements Parser
+    implements Parser, XhtmlMarkup
 {
     /**
      * This stack is needed to keep track of the different link and anchor-types
@@ -61,6 +67,7 @@
      */
     private static final String ANCHOR = "anchor";
 
+    /** {@inheritDoc} */
     public void parse( Reader reader, Sink sink )
         throws ParseException
     {
@@ -70,7 +77,7 @@
 
             parser.setInput( reader );
 
-            parseXdoc( parser, sink );
+            parseXhtml( parser, sink );
         }
         catch ( Exception ex )
         {
@@ -78,8 +85,14 @@
         }
     }
 
-    public void parseXdoc( XmlPullParser parser, Sink sink )
-        throws Exception
+
+    /**
+     * @param parser
+     * @param sink
+     * @throws IOException if any
+     * @throws XmlPullParserException if any
+     */
+    public void parseXhtml( XmlPullParser parser, Sink sink ) throws XmlPullParserException, IOException
     {
         int eventType = parser.getEventType();
 
@@ -87,26 +100,26 @@
         {
             if ( eventType == XmlPullParser.START_TAG )
             {
-                if ( parser.getName().equals( "title" ) )
+                if ( parser.getName().equals( Tag.TITLE.toString() ) )
                 {
                     sink.title();
                 }
                 /*
-                 * The ADDRESS element may be used by authors to supply contact information 
+                 * The ADDRESS element may be used by authors to supply contact information
                  * for a model or a major part of a model such as a form. This element
                  *  often appears at the beginning or end of a model.
                  */
-                else if ( parser.getName().equals( "address" ) )
+                else if ( parser.getName().equals( Tag.ADDRESS.toString() ) )
                 {
                     sink.author();
                 }
-                else if ( parser.getName().equals( "body" ) )
+                else if ( parser.getName().equals( Tag.BODY.toString() ) )
                 {
                     sink.body();
                 }
-                else if ( parser.getName().equals( "h1" ) || parser.getName().equals( "h2" ) ||
-                    parser.getName().equals( "h3" ) || parser.getName().equals( "h4" ) ||
-                    parser.getName().equals( "h5" ) )
+                else if ( parser.getName().equals( Tag.H1.toString() ) || parser.getName().equals( Tag.H2.toString() ) ||
+                    parser.getName().equals( Tag.H3.toString() ) || parser.getName().equals( Tag.H4.toString() ) ||
+                    parser.getName().equals( Tag.H5.toString() ) )
                 {
                     this.closeSubordinatedSections( parser.getName(), sink );
                     this.startSection( this.sections.size(), sink );
@@ -114,58 +127,58 @@
                     this.sections.push( parser.getName() );
 
                 }
-                else if ( parser.getName().equals( "p" ) )
+                else if ( parser.getName().equals( Tag.P.toString() ) )
                 {
                     sink.paragraph();
                 }
                 /*
-                 * The PRE element tells visual user agents that the enclosed text is 
+                 * The PRE element tells visual user agents that the enclosed text is
                  * "preformatted". When handling preformatted text, visual user agents:
                  * - May leave white space intact.
                  * - May render text with a fixed-pitch font.
                  * - May disable automatic word wrap.
                  * - Must not disable bidirectional processing.
                  * Non-visual user agents are not required to respect extra white space
-                 * in the content of a PRE element.  
+                 * in the content of a PRE element.
                  */
-                else if ( parser.getName().equals( "pre" ) )
+                else if ( parser.getName().equals( Tag.PRE.toString() ) )
                 {
                     sink.verbatim( true );
                 }
-                else if ( ( parser.getName().equals( "code" ) ) || ( parser.getName().equals( "samp" ) ) ||
-                    ( parser.getName().equals( "tt" ) ) )
+                else if ( ( parser.getName().equals( Tag.CODE.toString() ) ) || ( parser.getName().equals( Tag.SAMP.toString() ) ) ||
+                    ( parser.getName().equals( Tag.TT.toString() ) ) )
                 {
                     sink.monospaced();
                 }
-                else if ( parser.getName().equals( "ul" ) )
+                else if ( parser.getName().equals( Tag.UL.toString() ) )
                 {
                     sink.list();
                 }
-                else if ( parser.getName().equals( "ol" ) )
+                else if ( parser.getName().equals( Tag.OL.toString() ) )
                 {
                     sink.numberedList( Sink.NUMBERING_DECIMAL );
                 }
-                else if ( parser.getName().equals( "li" ) )
+                else if ( parser.getName().equals( Tag.LI.toString() ) )
                 {
                     sink.listItem();
                 }
-                else if ( parser.getName().equals( "head" ) )
+                else if ( parser.getName().equals( Tag.HEAD.toString() ) )
                 {
                     sink.head();
                 }
-                else if ( ( parser.getName().equals( "b" ) ) || ( parser.getName().equals( "strong" ) ) )
+                else if ( ( parser.getName().equals( Tag.B.toString() ) ) || ( parser.getName().equals( Tag.STRONG.toString() ) ) )
                 {
                     sink.bold();
                 }
-                else if ( ( parser.getName().equals( "i" ) ) || ( parser.getName().equals( "em" ) ) )
+                else if ( ( parser.getName().equals( Tag.I.toString() ) ) || ( parser.getName().equals( Tag.EM.toString() ) ) )
                 {
                     sink.italic();
                 }
-                else if ( parser.getName().equals( "a" ) )
+                else if ( parser.getName().equals( Tag.A.toString() ) )
                 {
-                    String href = parser.getAttributeValue( null, "href" );
-                    String name = parser.getAttributeValue( null, "name" );
-                    String id = parser.getAttributeValue( null, "id" );
+                    String href = parser.getAttributeValue( null, Attribute.HREF.toString() );
+                    String name = parser.getAttributeValue( null, Attribute.NAME.toString() );
+                    String id = parser.getAttributeValue( null, Attribute.ID.toString() );
                     if ( href != null )
                     {
                         sink.link( href );
@@ -182,20 +195,20 @@
                         this.linktypes.push( XhtmlParser.ANCHOR );
                     }
                 }
-                else if ( parser.getName().equals( "br" ) )
+                else if ( parser.getName().equals( Tag.BR.toString() ) )
                 {
-                	sink.lineBreak();
+                    sink.lineBreak();
                 }
-                else if ( parser.getName().equals( "hr" ) )
+                else if ( parser.getName().equals( Tag.HR.toString() ) )
                 {
                     sink.horizontalRule();
                 }
-                else if ( parser.getName().equals( "img" ) )
+                else if ( parser.getName().equals( Tag.IMG.toString() ) )
                 {
                     sink.figure();
-                    String src = parser.getAttributeValue( null, "src" );
-                    String title = parser.getAttributeValue( null, "title" );
-                    String alt = parser.getAttributeValue( null, "alt" );
+                    String src = parser.getAttributeValue( null, Attribute.SRC.toString() );
+                    String title = parser.getAttributeValue( null, Attribute.TITLE.toString() );
+                    String alt = parser.getAttributeValue( null, Attribute.ALT.toString() );
                     if ( src != null )
                     {
                         sink.figureGraphics( src );
@@ -218,19 +231,19 @@
                 // Tables
                 // ----------------------------------------------------------------------
 
-                else if ( parser.getName().equals( "table" ) )
+                else if ( parser.getName().equals( Tag.TABLE.toString() ) )
                 {
                     sink.table();
                 }
-                else if ( parser.getName().equals( "tr" ) )
+                else if ( parser.getName().equals( Tag.TR.toString() ) )
                 {
                     sink.tableRow();
                 }
-                else if ( parser.getName().equals( "th" ) )
+                else if ( parser.getName().equals( Tag.TH.toString() ) )
                 {
                     sink.tableCell();
                 }
-                else if ( parser.getName().equals( "td" ) )
+                else if ( parser.getName().equals( Tag.TD.toString() ) )
                 {
                     sink.tableCell();
                 }
@@ -238,64 +251,64 @@
             }
             else if ( eventType == XmlPullParser.END_TAG )
             {
-                if ( parser.getName().equals( "title" ) )
+                if ( parser.getName().equals( Tag.TITLE.toString() ) )
                 {
                     sink.title_();
                 }
-                else if ( parser.getName().equals( "address" ) )
+                else if ( parser.getName().equals( Tag.ADDRESS.toString() ) )
                 {
                     sink.author_();
                 }
-                else if ( parser.getName().equals( "body" ) )
+                else if ( parser.getName().equals( Tag.BODY.toString() ) )
                 {
                     //close all sections that are still open
                     closeSubordinatedSections( "h0", sink );
                     sink.body_();
                 }
-                else if ( parser.getName().equals( "h1" ) || parser.getName().equals( "h2" ) ||
-                    parser.getName().equals( "h3" ) || parser.getName().equals( "h4" ) ||
-                    parser.getName().equals( "h5" ) )
+                else if ( parser.getName().equals( Tag.H1.toString() ) || parser.getName().equals( Tag.H2.toString() ) ||
+                    parser.getName().equals( Tag.H3.toString() ) || parser.getName().equals( Tag.H4.toString() ) ||
+                    parser.getName().equals( Tag.H5.toString() ) )
                 {
                     this.closeSectionTitle( this.sections.size() - 1, sink );
                 }
-                else if ( parser.getName().equals( "p" ) )
+                else if ( parser.getName().equals( Tag.P.toString() ) )
                 {
                     sink.paragraph_();
                 }
-                else if ( parser.getName().equals( "pre" ) )
+                else if ( parser.getName().equals( Tag.PRE.toString() ) )
                 {
                     sink.verbatim_();
                 }
-                else if ( ( parser.getName().equals( "code" ) ) || ( parser.getName().equals( "samp" ) ) ||
-                    ( parser.getName().equals( "tt" ) ) )
+                else if ( ( parser.getName().equals( Tag.CODE.toString() ) ) || ( parser.getName().equals( Tag.SAMP.toString() ) ) ||
+                    ( parser.getName().equals( Tag.TT.toString() ) ) )
                 {
                     sink.monospaced_();
                 }
-                else if ( parser.getName().equals( "ul" ) )
+                else if ( parser.getName().equals( Tag.UL.toString() ) )
                 {
                     sink.list_();
                 }
-                else if ( parser.getName().equals( "ol" ) )
+                else if ( parser.getName().equals( Tag.OL.toString() ) )
                 {
                     sink.numberedList_();
                 }
-                else if ( parser.getName().equals( "li" ) )
+                else if ( parser.getName().equals( Tag.LI.toString() ) )
                 {
                     sink.listItem_();
                 }
-                else if ( parser.getName().equals( "head" ) )
+                else if ( parser.getName().equals( Tag.HEAD.toString() ) )
                 {
                     sink.head_();
                 }
-                else if ( ( parser.getName().equals( "b" ) ) || ( parser.getName().equals( "strong" ) ) )
+                else if ( ( parser.getName().equals( Tag.B.toString() ) ) || ( parser.getName().equals( Tag.STRONG.toString() ) ) )
                 {
                     sink.bold_();
                 }
-                else if ( ( parser.getName().equals( "i" ) ) || ( parser.getName().equals( "em" ) ) )
+                else if ( ( parser.getName().equals( Tag.I.toString() ) ) || ( parser.getName().equals( Tag.EM.toString() ) ) )
                 {
                     sink.italic_();
                 }
-                else if ( parser.getName().equals( "a" ) )
+                else if ( parser.getName().equals( Tag.A.toString() ) )
                 {
                     String linktype = (String) this.linktypes.pop();
                     //the equals operation is ok here, because we always use the class constant
@@ -312,19 +325,19 @@
                 // Tables
                 // ----------------------------------------------------------------------
 
-                else if ( parser.getName().equals( "table" ) )
+                else if ( parser.getName().equals( Tag.TABLE.toString() ) )
                 {
                     sink.table_();
                 }
-                else if ( parser.getName().equals( "tr" ) )
+                else if ( parser.getName().equals( Tag.TR.toString() ) )
                 {
                     sink.tableRow_();
                 }
-                else if ( parser.getName().equals( "th" ) )
+                else if ( parser.getName().equals( Tag.TH.toString() ) )
                 {
                     sink.tableCell_();
                 }
-                else if ( parser.getName().equals( "td" ) )
+                else if ( parser.getName().equals( Tag.TD.toString() ) )
                 {
                     sink.tableCell_();
                 }
@@ -346,23 +359,23 @@
      */
     private static void text( Sink sink, String text )
     {
-    	if( text.startsWith( "&nbsp;" ) )
-    	{
-    		sink.nonBreakingSpace();
-    	}
-    	String[] s = text.split( "&nbsp;" );
-    	for( int i = 0; i < s.length; i++ )
-    	{
-        	sink.text( s[i] );
-        	if( i + 1 < s.length )
-        	{
-        		sink.nonBreakingSpace();
-        	}
-		}
-    	if( text.endsWith( "&nbsp;" ) )
-    	{
-    		sink.nonBreakingSpace();
-    	}
+        if( text.startsWith( "&nbsp;" ) )
+        {
+            sink.nonBreakingSpace();
+        }
+        String[] s = text.split( "&nbsp;" );
+        for( int i = 0; i < s.length; i++ )
+        {
+            sink.text( s[i] );
+            if( i + 1 < s.length )
+            {
+                sink.nonBreakingSpace();
+            }
+        }
+        if( text.endsWith( "&nbsp;" ) )
+        {
+            sink.nonBreakingSpace();
+        }
     }
 
     private void closeSubordinatedSections( String level, Sink sink )



Mime
View raw message