Hi,

early 2000' I've used both Anakia derived XML rendering and DSVL.

The major characteristics are:
* Anakia uses JDOM which has more Java-like syntax (node children are just 
plain collections). Anakia was also built as an ANT task, but can be used alone 
as the documentation shows.
* DSVL can be used in an XSLT-like declarative form, internally it uses XML4J 
which has build in XPATH support

I liked DSVL more since it allows both declarative processing and explicit 
modes. It has been on the back of my mind to implement an upgrade of DSVL using 
standard DOM and JXPath to profit from the standards and also to use the 
XSL-style processing on Java objects. But I never had the project nor the time 
to do this...

The speed of DSVL was a bit dissapointing, but XSLT was not much better. For 
one-time conversion tasks this should be no issue.

Another option apart from using Anakia and DSVL, is to just place your DOM into 
the context along with some XPATH tool and do your processing from there. I 
currently use such an approach with a custom cmdline tool (that understands XML 
and CSV - see attachment, plus some old-examples). Again, in the back of my 
mind I want to move this to use the ToolboxManager to be more configurable and 
flexible.

The idea of using velocity for XML rendering into HTML is much neater than XSL 
since the syntaxes do not collide and are optically easy to kept apart.

Just mu 2c!
:) Christoph


Alessandro Bologna wrote:
> Thanks Rob!
> 
> Any other suggestions from the list?
> Alessandro
> 
> 
> 
> On 9/12/07, Robert Koberg <[EMAIL PROTECTED]> wrote:
>> On Wed, 2007-09-12 at 13:25 -0400, Alessandro Bologna wrote:
>>
>>> In any case, what I am really interested is what are the best practices
>>> using Velocity to render complex, quite unstructured,  namespace
>>> qualified XML contents.
>> Your going to run into problems with DVSL/Anakia if your content is
>> unstructured
>>
>>> What was your approach?
>> XSL is the tool for the job.
>>
>> best,
>> -Rob
>>
>>> Thanks in advance for your responses.
>>>
>>> Alessandro
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>> On 9/11/07, Alessandro Bologna <[EMAIL PROTECTED]> wrote:
>>>> Hi,
>>>>
>>>> my apologies in advance if this is an FAQ, but I couldn't find an
>> answer
>>>> in the documentation, so I decided to write to the list.
>>>>
>>>> In the CMS project I am working on, we are considering using velocity
>> to
>>>> generate "pre-cooked" template based web pages, using XML as data
>> source.
>>>> In other words, the data to be published is essentially XML, with
>> elements
>>>> such as <article>, <headline>, <deck>, <body> etc, with some elements
>>>> containing quite unstructured XHTML (i.e variable number of <p/>,
>> <div/>
>>>> etc), and other containing refences to other elements via id/refid.
>>>>
>>>> This data has to be processed to generate static HMTL files that are
>> then
>>>> pushed to Apache for serving them.
>>>>
>>>> Question #1
>>>> It seems that there are two main options: using XMLEasyBean or/and
>> DVSL.
>>>> Are there any other alternatives?
>>>>
>>>> Question #2
>>>> In both cases, it seems that XML namespaces are not supported (I am
>>>> getting an empty output if my source contains namespace qualified XML
>>>> elements), am I doing something wrong?
>>>>
>>>> Question #3
>>>> Any idea of performances, compared to say, XSLT?
>>>>
>>>> Thanks
>>>> Alessandro Bologna
>>>>
>>>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: [EMAIL PROTECTED]
>> For additional commands, e-mail: [EMAIL PROTECTED]
>>
>>
> 
/*
 * 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.    
 */

package de.dlr.dfd.dims.tools.transformer;

import java.io.BufferedWriter;
import java.io.File;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Hashtable;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.Vector;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.anakia.AnakiaJDOMFactory;
import org.apache.velocity.anakia.Escape;
import org.apache.velocity.anakia.OutputWrapper;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.util.StringUtils;

import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

import org.xml.sax.SAXParseException;

import de.dlr.dfd.dims.tools.xmlTransformer.ClassTool;
import de.dlr.dfd.dims.tools.xmlTransformer.ContextTool;
import de.dlr.dfd.dims.tools.csvParser.CSVParser;



/**
 * The purpose of this class is to allow you to use Velocity as an XML
 * transformation tool like XSLT is. So, instead of using XSLT, you will
 * be able to use this class instead to do your transformations.<p>
 *
 * You can find more documentation about used classes on the Velocity <a
 * href="http://jakarta.apache.org/velocity/anakia.html";>Website</a> .
 * 
 * Addition (28.01.2005) : You can also use this class to transform a csv-file.
 * The old name of this file was xmlTransformer in the same package. (Sven 
Kroeger)
 *
 * @created   November 13, 2001
 * @author    Jon S. Stevens, Attila Szegedi, Christoph Reck, Sven Kroeger
 */
public class Transformer
{
  static final String CLASS_ID = "@(#) $Id: Transformer.java,v 1.2 2005/02/01 
13:46:39 reck Exp $";

  private static String XML = "xml";
  private static String CSV = "csv";
  
  /** The VelocityEngine instance to use. **/
  private VelocityEngine velocity;

  /** The <code>[EMAIL PROTECTED] SAXBuilder}</code> instance to use. **/
  private SAXBuilder builder;

  /** The encoding used for the output **/
  protected String encoding = "ISO-8859-1";

  /**
   * Create the transformer instance, then use
   * <code>process(fileIn, templateIn)</code>.
   */
  public Transformer(String templatePath, String loggerClass, String 
kindOfSource)
    throws Exception
  {
    // create the VelocityEngine instance to use
    velocity = new VelocityEngine();

    if ( kindOfSource.equals( XML ) )  // kroeger : add if-clause
    {
        // create the AnakiaElement factory
        builder = new SAXBuilder();
        builder.setFactory( new AnakiaJDOMFactory() );
    }   

    // either use velocity.properties from the template path or defaults
    File templateDir = new File(templatePath);
    File velocityPropertiesFile = new File(templateDir, "velocity.properties");
    if( velocityPropertiesFile.exists() )
    {
      velocity.init( velocityPropertiesFile.getAbsolutePath() );
    }
    else
    {
      templatePath = new File(templatePath).getCanonicalPath();
      velocity.setProperty( RuntimeConstants.FILE_RESOURCE_LOADER_PATH,
        templatePath );
      velocity.setProperty( RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS,
        loggerClass );
      velocity.setProperty( RuntimeConstants.VM_PERM_ALLOW_INLINE, "true");
      velocity.setProperty( 
RuntimeConstants.VM_PERM_ALLOW_INLINE_REPLACE_GLOBAL, "true");
      velocity.init();
    }

    // get the property TEMPLATE_ENCODING, we know it's a string...
    String encoding = (String) 
velocity.getProperty(RuntimeConstants.OUTPUT_ENCODING);
    if( (encoding == null) || (encoding.length() == 0) ||
         encoding.equals("8859-1") || encoding.equals("8859_1") )
    {
      encoding = "ISO-8859-1";
    }
  }

  /**
   * Process an XML file using Velocity.
   */
  private void processXML( Map rootContext,
                           String xmlFile,
                           String transformTemplate,
                           OutputStream outStream )
  {
    Writer writer = null;
    try
    {
      //-- command line status
      velocity.info( "Transformer input: " + xmlFile );

      // Build the JDOM Document
      Document document = builder.build( xmlFile );

      // Shove things into the Context
      VelocityContext context = new VelocityContext(rootContext);

      OutputWrapper ow = new OutputWrapper();
      ow.setEncoding( encoding );

      context.put( "document", document );
      context.put( "xmlout", ow );
      context.put( "relativePath", getRelativePath( xmlFile ) );
      context.put( "escape", new Escape() );
      context.put( "Context", new ContextTool(context) );
      context.put( "date", new java.util.Date() );

      /*
       * Make the context instance available in the templates.
       * TODO: provide a pluggable directive that makes this.
       * Note: the per-request context initializer template can
       * remove it to hide it from the application templates.
       */
      context.put( "context", context );

      // Process the VSL template with the context and write out
      // the result as the outFile.
      writer = new BufferedWriter( new OutputStreamWriter( outStream ) );
      // get the template to process
      Template template = velocity.getTemplate( transformTemplate );
      template.merge( context, writer );

      velocity.info( "Transformer done." );
    }
    catch( JDOMException e )
    {
      if( e.getCause() != null )
      {
        Throwable rootCause = e.getCause();
        if( rootCause instanceof SAXParseException )
        {
          System.err.println( "" );
          System.err.println( "Error: " + rootCause.getMessage() );
          System.err.println(
            "       Line: " +
            ( ( SAXParseException ) rootCause ).getLineNumber() +
            " Column: " +
            ( ( SAXParseException ) rootCause ).getColumnNumber() );
          System.err.println( "" );
        }
        else
        {
          rootCause.printStackTrace();
        }
      }
      else
      {
        e.printStackTrace();
      }
      velocity.error( "Transformer failed to process " + xmlFile );
    }
    catch( Throwable e )
    {
      velocity.error( "Transformer failed to process " + xmlFile );
      e.printStackTrace();
    }
    finally
    {
      if( writer != null )
      {
        try
        {
          writer.flush();
          writer.close();
        }
        catch( Exception e )
        {
        }
      }
    }
  }

  /**
   * Process an CSV file using Velocity.
   */
  private void processCSV( Map          rootContext,
                           String       csvFile,
                           String       transformTemplate,
                           OutputStream outStream )
  {
    Writer writer = null;
    try
                {
          //-- command line status
          velocity.info( "Transformer input: " + csvFile );
        
          // Shove things into the Context
          VelocityContext context = new VelocityContext(rootContext);
          
          OutputWrapper ow = new OutputWrapper();
          ow.setEncoding( encoding );
          
          context.put( "inFile", csvFile );
          context.put( "csvParser", new CSVParser() );
          
          context.put( "csvout", ow );
          context.put( "relativePath", getRelativePath( csvFile ) );
          context.put( "escape", new Escape() );
          context.put( "Context", new ContextTool(context) );
          context.put( "date", new java.util.Date() );
        
          /*
           * Make the context instance available in the templates.
           * TODO: provide a pluggable directive that makes this.
           * Note: the per-request context initializer template can
           * remove it to hide it from the application templates.
           */
          context.put( "context", context );

          // Process the VSL template with the context and write out
          // the result as the outFile.
          writer = new BufferedWriter( new OutputStreamWriter( outStream ) );
          // get the template to process
          Template template = velocity.getTemplate( transformTemplate );
          template.merge( context, writer );
        
          velocity.info( "Transformer done with CSV file." );
                }
          catch( Throwable e )
    {
      velocity.error( "Transformer failed to process " + csvFile );
      e.printStackTrace();
    }
    finally
    {
      if( writer != null )
      {
        try
        {
          writer.flush();
          writer.close();
        }
        catch( Exception e )
        {
        }
      }
    }
  }

  /**
   * Process a controller template using Velocity.
   */
  private void process( Map rootContext,
                        String transformTemplate,
                        OutputStream outStream )
  {
    Writer writer = null;
    try
    {
      // Shove things into the Context
      VelocityContext context = new VelocityContext(rootContext);

      context.put( "relativePath", getRelativePath( transformTemplate ) );
      context.put( "escape", new Escape() );
      context.put( "Context", new ContextTool(context) );
      context.put( "date", new java.util.Date() );

      /*
       * Make the context instance available in the templates.
       * TODO: provide a pluggable directive that makes this.
       * Note: the per-request context initializer template can
       * remove it to hide it from the application templates.
       */
      context.put( "context", context );

      // Process the VSL template with the context and write out
      // the result as the outFile.
      writer = new BufferedWriter( new OutputStreamWriter( outStream ) );
      // get the template to process
      Template template = velocity.getTemplate( transformTemplate );
      template.merge( context, writer );

      velocity.info( "Transformer done." );
    }
    catch( Throwable e )
    {
      velocity.error( "Transformer failed to process " + transformTemplate );
      e.printStackTrace();
    }
    finally
    {
      if( writer != null )
      {
        try
        {
          writer.flush();
          writer.close();
        }
        catch( Exception e )
        {
        }
      }
    }
  }

  /**
   * Hacky method to figure out the relative path that we are currently in. This
   * is good for getting the relative path for images and anchor's.
   *
   * @param file
   * @return      The <code>relativePath</code> value.
   */
  private String getRelativePath( String file )
  {
    if( file == null || file.length() == 0 )
    {
      return "";
    }
    StringTokenizer st = new StringTokenizer( file, "/\\" );
    // needs to be -1 cause ST returns 1 even if there are no matches. huh?
    int slashCount = st.countTokens() - 1;
    StringBuffer sb = new StringBuffer();
    for( int i = 0; i < slashCount; i++ )
    {
      sb.append( "../" );
    }

    if( sb.toString().length() > 0 )
    {
      return StringUtils.chop( sb.toString(), 1 );
    }
    else
    {
      return ".";
    }
  }

  /**
   * Transformation invocation over command line arguments.
   */
  public static void main(String[] args)
  {
        // A switch for the kind of transforming;
        String kindOfSource = "";
    // The classname of the default logger to be used
    String logger = "org.apache.velocity.runtime.log.NullLogSystem";

    Vector vargs = new Vector();
    for( int i = 0; i < args.length; ++i )
    {
      if( args[i].equals("-quiet") )
        logger = "org.apache.velocity.runtime.log.NullLogSystem";
      else if( args[i].equals("-verbose") )
        logger = "de.dlr.dfd.dims.tools.xmlTransformer.StderrLogSystem";
      else if ( args[i].equalsIgnoreCase("-xml") )
      {
        kindOfSource = XML;
      }
      else if ( args[i].equalsIgnoreCase("-csv") )
      {
        kindOfSource = CSV;
      }
      else
        vargs.add( args[i] );
    }
    if( vargs.size() >= 1 )
    {
      try
      {
                Transformer t = new Transformer(".", logger, kindOfSource);
                Hashtable rootContext = new Hashtable();
                // convenience to allow more complex actions within templates
                rootContext.put( "Class", new ClassTool() );
                rootContext.put( "args", args );
                String template, inFile;
                if( vargs.size() == 1 )
                {
                  inFile = "";
                  template = (String) vargs.elementAt(0);
                }
                else
                {
                  inFile = (String) vargs.elementAt(0);
                  template = (String) vargs.elementAt(1);
                }
      
                if ( kindOfSource.equals( CSV ) )
                {
                  // run a CSV-Transformation
                  t.processCSV( rootContext, inFile, template, System.out );
                }
                else if ( kindOfSource.equals( XML ) )
                {
                  // run a XML-Transformation
                  t.processXML( rootContext, inFile, template, System.out );
                }
                else
                {
                  t.process( rootContext, template, System.out );
                }
      }
      catch ( Exception ex )
      {
              System.err.println("ERROR: failed running transformation");
              ex.printStackTrace();
              Runtime.getRuntime().exit(2);
      }
    }
    else
    {
      System.err.println("ERROR: invalid command line parameters");
      System.err.println("");
      System.err.println("USAGE: Transformer [-xml file.xml | -csv file.csv] 
transformTemplate.vsl");
      System.err.println("");
      Runtime.getRuntime().exit(1);
    }
  }

} // end of XmlTransformer
## ------------------------------------------------------------------------
## Function to read in a CVS file into an array of maps.
## The first item in the array contains the header line
## ------------------------------------------------------------------------
#set( $LF = $Context.formDecode("%0A") )
#macro( parseCSV $filename $lines )
  Parsing file="$filename"
  #set( $text = "#include($filename)" )
  #set( $tokenizer = $Class.newInstance("java.util.StringTokenizer", $text, 
";") )
  ##
  ## loop over all items in text
  #set( $header = true )
  #set( $headers = [] )
  #foreach( $field in $tokenizer )
    ##
    ## possibly split last $item in a line from $next
    #set( $index = $field.indexOf($LF) )
    #if( $index > 0 )
      #set( $item = $field.substring(0, $index) )
      #set( $index = $index + 1 )
      #set( $next = $field.substring($index) )
    #else
      #set( $item = $field )
    #end
    ##
    ## process item
    #if( $header )
      #call( $headers.add($item) )
    #else
      ## process line
      #set( $column = $column + 1 )
      #call( $map.put( $headers.get($column), $item ) )
    #end
    ##
    ## process new line
    #if( $index > 0 )
      #if( $header )
        #set( $header = false )
        #call( $lines.add($headers) )
      #else
        #call( $lines.add($map) )
      #end
      #set( $column = 0 )
      #set( $map = $Context.newHashtable( $headers.size() ) )
      #call( $map.put( $headers.get($column), $next ) )
    #end
  #end
#end## end of macro parseCSV.vm

## ------------------------------------------------------------------------
## Macro to write a text to a file.
## ------------------------------------------------------------------------
#macro( fileWrite $filename $text )
  #set( $out = $Class.newInstance("java.io.FileOutputStream", $filename) )
  #set( $data = $text.getBytes() )
  #call( $out.write($data) )
  #call( $out.close() )
#end

## ------------------------------------------------------------------------
## Macro to convert a CVS-map to an HTML table.
## ------------------------------------------------------------------------
#macro( convertToCsvTable $lines )
  <table>
    <tr>
  #foreach( $header in $lines.get(0) )
      <th>$header</th>
  #end
    </tr>
  #set( $rows = $lines.size() - 1 )
  #foreach( $row in [1..$rows] )
    <tr>
    #set( $map = $lines.get($row) )
    #foreach( $header in $lines.get(0) )
      <td id="$header">$map.get($header)</td>
    #end
    </tr>
  #end
  </table>
#end

## transform file to 2-dimensional array
#set( $csvFilename = "test.csv" )
#set( $outFilename = "test.xml" )
#set( $lines = [] )
#set( $dummy = "#parseCSV($csvFilename $lines)" )

#if( $lines.size() == 0 )
  Failed reading CSV file "$csvFilename"
  ## debug code
  #### #parse("dump.vm")
#else
  ## output the array
  #set( $text = "#convertToCsvTable( $lines )" )
  $text
  ## write to file
  #fileWrite( $outFilename $text )
#end
## ------------------------------------------------------------------------
## File:        templates/control/explain.vm
##
## Description: Controller to show details on an attribute.
##
## Input:       QueryData: attributeId and attributeValue
##              Loads the 'gipValids' using a #parse( (possibly ) from cache)
##
## Output:      Attribute help screen.
##
## Date:        2000-11-08
## Author:      [EMAIL PROTECTED]
## Copyright:   (c) 2000 Deutsches Zentrum fuer Luft und Raumfahrt
## ------------------------------------------------------------------------
##
#set( $title = "GIP Attribute Descriptor" )
##
## -------- import attribute valids (cached) --------
#parse( "control/include/gipValids.vm" )
##
#set( $l_attributeValue = $parameters.getString("AttributeValue", "") )
#if( $l_attributeValue == "" )
  #error( "No attributeValue defined; nothing to lookup for help." )
####  #stop()
#end
#set( $l_attributeId = $parameters.getString("AttributeId", "") )
#if( $l_attributeId == "" )
  #set( $l_attributeId = '*AID' )
#end
##
#set( $l_xpathSpec = "AttributeValueDescriptor[AttributeId='$l_attributeId' and 
AttributeValue='$l_attributeValue']/*[name()!='DocumentReference']" )
#set( $l_xmlList = $Xml.applyXPath($gipValids, $l_xpathSpec) )
#if( $l_xmlList.isEmpty() )
  #error( "No descriptors found for AttributeId=$l_attributeId and 
AttributeValue=$l_attributeValue" )
####  #stop()
#end

  <table bgcolor="$COLORS.BG_TABLE" border="0" cellspacing="1" cellpadding="2">
    <tr bgcolor="$COLORS.BG_HEAD">
      <td><font color="$COLORS.FG_HEAD">Name</font></td>
      <td><font color="$COLORS.FG_HEAD">Value</font></td>
    </tr>
   #foreach( $l_field in $l_xmlList )
     #set( $l_name = $l_field.getName() )
     #set( $l_text = $l_field.getText() )
    <tr>
      <td style="vertical-align:top">$l_name</td>
      <td>
        <tt>
         #if( $l_text == "" )
          <font color="red">empty</font>
         #elseif( $l_name == "AttributeId" )
           ## make AttributeId a backward link
          <a href="$link.clone().addQueryData('AttributeValue',$l_text)"
           >$l_attributeId</a>
         #elseif( $l_name == "Abstract" )
           ## format in wrapping fixed-font, but preserving spaces
           #set( $NL = $Context.formDecode('%0A') )
           #set( $l_separator = '' )
           #foreach( $l_line in $Context.tokenize($l_text, $NL) )
          $Regexp.substitute('s/  /&nbsp;&nbsp;/g', $l_line)$l_separator
             #set( $l_separator = '<br>' )
           #end
         #else
          $l_text
         #end
        </tt>
      </td>
    </tr>
   #end
## list of DocumentReferences
    <tr>
      <td style="vertical-align:top">DocumentReference</td>
      <td>
        </tt>
#set( $l_xmlList = $l_xmlList.get(0).Parent.getChildren("DocumentReference") )
     #if( $l_xmlList.isEmpty() )
        <em>none</em>
     #else
       #foreach( $l_field in $l_xmlList )
         #if( $velocityCount != 1 )
          <br>
         #end
         #if( $l_field.getAttribute("DocumentPointer") )
          <a href="$l_field.getAttributeValue("DocumentPointer")">
           #if( $l_field.getAttribute("DocumentDescription") )
            $l_field.getAttributeValue("DocumentDescription")
           #else
            $l_field.getAttributeValue("DocumentPointer")
           #end
          </a>
         #else
          <font color="red">empty</font>
         #end
       #end
     #end
      </td>
    </tr>
## list of valids
    <tr>
      <td colspan="2"></td>
    </tr>
    <tr>
      <td style="vertical-align:top" align="left">Valids</td>
      <td>
#set( $l_xpathSpec = 
"AttributeValueDescriptor[AttributeId='$l_attributeValue']" )
#set( $l_xmlList = $Xml.applyXPath($gipValids, $l_xpathSpec) )
     #if( $l_xmlList.isEmpty() )
        <em>none</em>
     #else
       #foreach( $l_field in $l_xmlList )
         #if( $velocityCount != 1 )
        <br>
         #end
         #set( $l_attributeId = $l_field.getChildText("AttributeId") )
         #set( $l_attributeValue = $l_field.getChildText("AttributeValue") )
         #set( $l_shortName = $l_field.getChildText("ShortName") )
         #if ( $l_field.getChildText("LongName") ) ## avoid log messages
           #set( $l_longName  = $l_field.getChildText("LongName") )
         #else
           #set( $l_longName  = false )
         #end
         #set( $l_url = $link.clone() )
         #call( $l_url.addQueryData('AttributeId',$l_attributeId) )
         #call( $l_url.addQueryData('AttributeValue',$l_attributeValue) )
        <tt><a href="$l_url" >$l_shortName</a></tt>
         #if( $l_longName && ($l_shortName != $l_longName) )
        &nbsp;<font size="-1">$l_longName</font>
         #end
       #end
     #end
      </td>
    </tr>
  </table>
/*
 * $Header: $
 * $Revision: 1.0 $
 * $Date: 2001/06/13 $
 *
 * ====================================================================
 *
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Commons", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact [EMAIL PROTECTED]
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 */
package org.apache.commons.tools;

import java.io.InputStream;
import java.io.FileInputStream;
import java.io.CharArrayReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Attribute;
import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter;
import com.werken.xpath.XPath;

import org.jdom.input.DOMBuilder; // ### for JAXP security workaround
import java.io.ByteArrayInputStream;

// to emit error messagesd to the log
import org.apache.velocity.runtime.Runtime;

/**
 * This is a class to provide convenient access and control
 * over XML files via <a href="http://www.jdom.org";>JDOM</a>.
 *
 * @company      Deutsches Zentrum fuer Luft- und Raumfahrt<p>
 * @author       [EMAIL PROTECTED]
 */
public class XmlTool
{
    /**
     * Empty constructor.
     */
    public XmlTool()
    {
        // nothing to do
    }

    /**
     * Load a JDOM document.
     *
     * @param fileName The name of the file to load.
     * @return The JDOM document root element.
     */
    public static Element load(String fileName)
    {
        InputStream in = null;
        try
        {
            try
            {
                URL url = new URL(fileName);
                in = url.openStream();
            }
            catch( Exception ignore )
            {
                in = new FileInputStream(fileName);
            }
            SAXBuilder builder =
                new SAXBuilder("org.apache.crimson.parser.XMLReaderImpl");
            Document doc = builder.build(in);
            return doc.getRootElement();
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }

        return null;
    }

    /**
     * Create a JDOM tree from a string.
     *
     * @param xmlString The name of the file to load.
     * @return The JDOM document root element.
     */
    public static Element fromString(String xmlString)
    {
        try
        {
            CharArrayReader in = new CharArrayReader(xmlString.toCharArray());
            SAXBuilder builder =
                new SAXBuilder("org.apache.crimson.parser.XMLReaderImpl");
            Document doc = builder.build(in);
            return doc.getRootElement();
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }

        return null;
    }

    /**
     * Pretty print a JDOM tree.
     *
     * @param jdom XML document.
     * @param indent The indentation string (e.g. spaces or tabs)
     * @param initalIndent The initialIndent level.
     * @return The formatted string (an empty string upon failure).
     */
    public static String format(Document jdom, String indent, int initalIndent)
    {
        try
        {
            XMLOutputter serializer = new XMLOutputter();
            serializer.setIndent(indent);
            serializer.setIndentLevel(initalIndent);
            return serializer.outputString(jdom);
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }

        return "";
    }

    /**
     * Pretty print a JDOM tree.
     *
     * @param jdom JDOM document root element.
     * @param indent The indentation string (e.g. spaces or tabs)
     * @param initalIndent The initialIndent level.
     * @return The formatted string (an empty string upon failure).
     */
    public static String format(Element jdom, String indent, int initalIndent)
    {
        try
        {
            XMLOutputter serializer = new XMLOutputter();
            serializer.setIndent(indent);
            serializer.setIndentLevel(initalIndent);
            return serializer.outputString(jdom);
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }

        return "";
    }

    /**
     * Retrieve a list of attributes specified via an XPath statement.
     *
     * @param jdom JDOM document root element.
     * @param xpathSpec The XPath specification to the desired attributes.
     * @return An <code>ArrayList</code> with the attribute values.
     */
    public static ArrayList getAttributeList(Element jdom, String xpathSpec)
    {
        List attributes = applyXPath(jdom, xpathSpec);

        ArrayList list = new ArrayList( attributes.size() );

        ListIterator iter = attributes.listIterator();
        while( iter.hasNext() )
        {
            Object item = iter.next();
            if (item instanceof Attribute)
                list.add( ((Attribute) item).getValue() );
        }

        return list;
    }

    /**
     * Retrieve a list of text elements specified via an XPath statement.
     *
     * @param jdom JDOM document root element.
     * @param xpathSpec The XPath specification to the elements/attributes.
     * @return An <code>ArrayList</code> with the text of the elements.
     */
    public static ArrayList getTextList(Element jdom, String xpathSpec)
    {
        List elements = applyXPath(jdom, xpathSpec);

        ArrayList list = new ArrayList( elements.size() );

        ListIterator iter = elements.listIterator();
        while( iter.hasNext() )
        {
            Object item = iter.next();
            if (item instanceof Element)
                list.add( ((Element) item).getText() );
            else if (item instanceof Attribute)
                list.add( ((Attribute) item).getValue() );
        }

        return list;
    }

    /**
     * Apply an XPath to a JDOM Document
     *
     * @param doc The JDOM Document.
     * @param xpathSpec The XPath to apply.
     *
     * @return A list of selected nodes.
     */
    public static List applyXPath(Document doc, String xpathSpec)
    {
        try
        {
            XPath xpath = new XPath(xpathSpec);
            return xpath.applyTo(doc);
        }
        catch (Exception ex)
        {
            Runtime.error("Error while processing applyXPath(\"" +
              xpathSpec + "\", " + doc.getRootElement().getName() + "): " +
              ex.toString() );
        }
        return null;
    }

    /**
     * Apply an XPath to a JDOM Element.
     *
     * @param elem The Element context.
     * @param xpathSpec The XPath to apply.
     *
     * @return A list of selected nodes.
     */
    public static List applyXPath(Element elem, String xpathSpec)
    {
        try
        {
            XPath xpath = new XPath(xpathSpec);
            return xpath.applyTo(elem);
        }
        catch (Exception ex)
        {
            Runtime.error("Error while processing applyXPath(\"" +
              xpathSpec + "\", " + elem.getName() + "): " +
              ex.toString() );
        }
        return null;
    }

    /**
       Apply an XPath to a nodeset.

       @param xpathSpec The XPath to apply.
       @param doc The nodeset context.

       @return A list of selected nodes.
    */
    public static List applyXPath(List nodeSet, String xpathSpec)
    {
        try
        {
            XPath xpath = new XPath(xpathSpec);
            return xpath.applyTo(nodeSet);
        }
        catch (Exception ex)
        {
            Runtime.error("Error while processing applyXPath(\"" +
              xpathSpec + "\", (List)): " +
              ex.toString() );
        }
        return null;
    }

} // end of XmlTool.java
## Tidy up DVSL template for the XmlConfigWriter output.
## The root node must be called ROOT.
## Usage:
## dvsl -STYLE xmlConfig_cleanup.dvsl -IN <in> -OUT <out>
## cat <out> | ~reck/ftp/www/tidy/tidy -xml -quiet -wrap 200 -indent > <clean>

## Double quote for convenience
#set( $qq = '"' )

## Macro to output argument if test evaluates to true
#macro( ifset $test $output )
#if ( $test )$output#end
#end

## print the ID attribute only if it is referenced
#macro( ID $element )#*
  *##if( $element.attrib('ID') )#*
    *##set( $ID = $element.attrib('ID') )#*
    *##if( ($ID == 'ROOT') || ($element.selectNodes("//[EMAIL 
PROTECTED]'$ID']").size() > 1) )#*
      *# ID="$ID"#*
    *##end#*
  *##end#*
*##end
#### disable IDs
#macro( ID $element )#**##end

# possibly emit a NAME attribute
#macro( NAME $element )#*
  *##if( $element.attrib('NAME') )#*
    *# NAME="$element.attrib('NAME')"#*
  *##end#*
*##end

# possibly emit a CLASS attribute
#macro( CLASS $element )#*
  *##if( $element.attrib('CLASS') )#*
    *# CLASS="$element.attrib('CLASS')"#*
  *##end#*
*##end

#match( "CONFIGURATION" )
<CONFIGURATION>
  $context.applyTemplates("[EMAIL PROTECTED]'ROOT']")
</CONFIGURATION>
#end

#match( "[EMAIL PROTECTED]'java.lang.String']" )
#set( $value = $node.value() )
#if( $value.trim() == "" )
  <STRING#NAME($node) VALUE="" />
#else
  <STRING#NAME($node)>$value</STRING>
#end
#end

#match( "[EMAIL PROTECTED]" )
#set( $ID = $node.attrib('REF') )
#if( $node.selectNodes("//[EMAIL PROTECTED]'$ID']").size() == 1 )
  $context.applyTemplates( $node.selectSingleNode("//[EMAIL PROTECTED]'$ID']") )
#else
  <OBJECT#ID($node)#NAME($node) REF="$ID" />
#end
#end

#match( "OBJECT" )
#if( $node.value().trim() != "" )
  <OBJECT#ID($node)#NAME($node)#CLASS($node)>$node.value()</OBJECT>
#else
  <OBJECT#ID($node)#NAME($node)#CLASS($node)>
    $context.applyTemplates("*")
  </OBJECT>
#end
#end

#match( "CONSTRUCTOR" )
#if( $node.children().isEmpty() )
  <CONSTRUCTOR#ID($node) />
#else
  <CONSTRUCTOR#ID($node)>
    $context.applyTemplates("*")
  </CONSTRUCTOR>
#end
#end

#match( "FIELD" )
  <FIELD#ID($node) NAME="$node.attrib('NAME')"#CLASS($node)>
    $context.applyTemplates("*")
  </FIELD>
#end

#match( "METHOD" )
  <METHOD#ID($node) NAME="$node.attrib('NAME')"#CLASS($node)>
    $context.applyTemplates("*")
  </METHOD>
#end

#match( "ARRAY" )
  <ARRAY#ID($node)#NAME($node)#CLASS($node)>
    $context.applyTemplates("*")
  </ARRAY>
#end

#match( "HASH" )
  <HASH#ID($node)#NAME($node)>
    $context.applyTemplates("*")
  </HASH>
#end


#match("*")
$node.copy()
#end

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to