You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@velocity.apache.org by Apache Wiki <wi...@apache.org> on 2005/11/07 16:53:35 UTC

[Jakarta-velocity Wiki] Update of "VelocityTools/MergeDirective" by DaigoKobayashi

Dear Wiki user,

You have subscribed to a wiki page or wiki category on "Jakarta-velocity Wiki" for change notification.

The following page has been changed by DaigoKobayashi:
http://wiki.apache.org/jakarta-velocity/VelocityTools/MergeDirective

New page:
This code is quick hack of Inculude directive. So this code need refinement.
Example usage can be found [http://www.abs-network.com/java/velocity.zip here].
This is a zipped eclipse project. You extract velocity.zip file and import to eclipse.
 
This project contain small parser and Main class.(I also contribute this small parser that create XML file.) First you run defalut package's Main class. It create Test.java file using 
test.vm template. Then you write some implementation between special comment(//velocity-merge [\S] start and end) to Test.java. Second you run org.apache.velocity.tools package's Main class. It create contents.xml that contain your implementation information. Then you run defalut package's Main class again. Your implementation doesn't lose. 
{{{
package org.apache.velocity.runtime.directive;

/*
 * Copyright 2000-2001,2005 The Apache Software Foundation.
 * 
 * Licensed 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 java.io.File;
import java.io.IOException;
import java.io.Writer;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.velocity.context.InternalContextAdapter;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.RuntimeServices;
import org.apache.velocity.runtime.parser.ParserTreeConstants;
import org.apache.velocity.runtime.parser.node.Node;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;


/**
 * Pluggable directive that handles the #merge() statement in VTL. 
 * This #merge() can take multiple arguments of either 
 * StringLiteral or Reference.
 *
 * Notes:
 * -----
 *  1) The included source material can only come from somewhere in 
 *    the TemplateRoot tree for security reasons. There is no way 
 *    around this.  If you want to include content from elsewhere on
 *    your disk, use a link from somwhere under Template Root to that 
 *    content.
 *
 *  2) By default, there is no output to the render stream in the event of
 *    a problem.  You can override this behavior with two property values :
 *       include.output.errormsg.start
 *       include.output.errormsg.end
 *     If both are defined in velocity.properties, they will be used to
 *     in the render output to bracket the arg string that caused the 
 *     problem.
 *     Ex. : if you are working in html then
 *       include.output.errormsg.start=<!-- #include error :
 *       include.output.errormsg.end= -->
 *     might be an excellent way to start...
 *
 *  3) As noted above, #include() can take multiple arguments.
 *    Ex : #include( "foo.vm" "bar.vm" $foo )
 *    will simply include all three if valid to output w/o any
 *    special separator.
 *
 * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
 * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
 * @author <a href="mailto:kav@kav.dk">Kasper Nielsen</a>
 * @version $Id: Include.java,v 1.26.4.1 2004/03/03 23:22:56 geirm Exp $
 */
public class Merge extends InputBase
{
    private String outputMsgStart = "";
    private String outputMsgEnd = "";

    /**
     * Return name of this directive.
     */
    public String getName()
    {
        return "merge";
    }        
    
    /**
     * Return type of this directive.
     */
    public int getType()
    {
        return BLOCK;
    }     
    
    /**
     *  simple init - init the tree and get the elementKey from
     *  the AST
     */
    public void init(RuntimeServices rs, InternalContextAdapter context,
                     Node node) 
        throws Exception
    {
        super.init( rs, context, node );
 
        /*
         *  get the msg, and add the space so we don't have to
         *  do it each time
         */
        outputMsgStart = rsvc.getString(RuntimeConstants.ERRORMSG_START);
        outputMsgStart = outputMsgStart + " ";
        
        outputMsgEnd = rsvc.getString(RuntimeConstants.ERRORMSG_END );
        outputMsgEnd = " " + outputMsgEnd;   
    }

    /**
     *  iterates through the argument list and renders every
     *  argument that is appropriate.  Any non appropriate
     *  arguments are logged, but render() continues.
     */
    public boolean render(InternalContextAdapter context, 
                           Writer writer, Node node)
        throws IOException, MethodInvocationException,
               ResourceNotFoundException, ParseErrorException
    {
        /*
         *  get our arguments and check them
         */

        int argCount = node.jjtGetNumChildren();
        
        if (argCount < 2) 
        {
            
            /*
             *  error - they didn't name the merge
             */
            
            rsvc.error("#merge error : Velocimerge must have name as 1st " + 
                "argument to #merge(). #args = " + argCount);
            outputErrorToStream( writer, "error");
        }

        /*
         *  we only handle StringLiterals and References right now
         */
        Node n = node.jjtGetChild(0);
        Node name = node.jjtGetChild(1);

        if ( n.getType() ==  ParserTreeConstants.JJTSTRINGLITERAL || 
             n.getType() ==  ParserTreeConstants.JJTREFERENCE )
        {
            if (!renderOutput( n, name, context, writer )) {
            	//fail to merge. So write default contents.
            	node.jjtGetChild(2).render(context, writer);
            	return true;
            }
        }
        else
        {
            rsvc.error("#include() error : invalid argument type : " 
                + n.toString());
            outputErrorToStream( writer, "error with arg " + 0 
                + " please see log.");
        }
        return true;
    }

    /**
     *  does the actual rendering of the included file
     *
     *  @param resoureceNameNode AST argument of type StringLiteral or Reference
     *  @param nameNode AST argument of type StringLiteral or Reference
     *  @param context valid context so we can render References
     *  @param writer output Writer
     *  @return boolean success or failure.  failures are logged
     */
    private boolean renderOutput( Node resourceNameNode, Node nameNode, InternalContextAdapter context, 
                                  Writer writer )
        throws IOException, MethodInvocationException,
               ResourceNotFoundException
    {
        String arg = "";
        String arg2 = "";
        
        if ( resourceNameNode == null )
        {
            rsvc.error("#merge() error :  null argument");
            return false;
        }
            
        /*
         *  does it have a value?  If you have a null reference, then no.
         */        
        Object value = resourceNameNode.value( context );
        if ( value == null)
        {
            rsvc.error("#merge() error :  null argument");
            return false;
        }
        
        /*
         *  does it have a value?  If you have a null reference, then no.
         */        
        Object value2 = nameNode.value( context );
        if ( value2 == null)
        {
            rsvc.error("#include() error :  null argument");
            return false;
        }

        /*
         *  get the path
         */
        arg = value.toString();
        
        arg2 = value2.toString();

        String content = getMergeContent(arg, arg2);
        if(content == null) {
        	return false;
        }
    	writer.write(content);
        return true;
    }

    /**
     *  Puts a message to the render output stream if ERRORMSG_START / END
     *  are valid property strings.  Mainly used for end-user template
     *  debugging.
     */
    private void outputErrorToStream( Writer writer, String msg )
        throws IOException
    {        
        if ( outputMsgStart != null  && outputMsgEnd != null)
        {
            writer.write(outputMsgStart);
            writer.write(msg);
            writer.write(outputMsgEnd);
        }
        return;
    }
    
    private String getMergeContent(String path, String name) {
    	try {
			DocumentBuilderFactory factory = DocumentBuilderFactory
					.newInstance();
			DocumentBuilder builder = factory.newDocumentBuilder();
			Document document = builder.parse(new File(path));
			Element root = document.getDocumentElement();
			NodeList list = root.getElementsByTagName("content");
			for (int i = 0; i < list.getLength(); i++) {
				Element element = (Element) list.item(i);
				String nameValue = element.getAttribute("name");
				if(nameValue.equals(name)) {
					return element.getTextContent();
				}
			}
		} catch (Exception ignore) {
			;
		}
    	return null;
    }
}
}}}

---------------------------------------------------------------------
To unsubscribe, e-mail: velocity-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: velocity-dev-help@jakarta.apache.org