#properties directive

Keats Kirsch <[email protected]> Sat, 30 Jul 2005 16:33:47 -0400
Newsgroups gmane.comp.java.webmacro.user
Message-ID <[email protected]>
I created a new directive called Properties directive.  This can be 
handy if you want to set a bunch of properties on an object within a 
template; essentially providing a abbreviated, readable syntax.  You can 
also combine it with the #include directive for processing a properties 
file.  (Just be careful of comments: use "##" or "# " to keep them from 
confusing the parser.)

I'm attaching the source and I will commit it to the core if folks agree 
it is worthy.

Here's some test WMScript that illustrates the usage:

## Test using default object: java.util.Properties
#set $Name="Keats Kirsch"
#set $Age=45
<h3>\#properties test</h3>
#properties $p {
    Name: $Name
    Age = $Age
}
My age is $p.Age<br>
My name is $p.Name<br>
## Test using a pre-existing POJO (non-map)
<h3>More \#properties</h3>
#bean $Cal="java.util.Calendar" scope=static
#set $GCal=$Cal.Instance
Today: $GCal.Time, FirstDayOfWeek: $GCal.FirstDayOfWeek, Lenient: 
$GCal.Lenient
<br>
#properties $GCal {
    FirstDayOfWeek=0
    Lenient=false
}
Today: $GCal.Time, FirstDayOfWeek: $GCal.FirstDayOfWeek, Lenient: 
$GCal.Lenient
<br>
PropertiesDirective.java (text/plain, 6.4 KB)
/*
 * Copyright (C) 2005 Semiotek Inc.  All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted under the terms of either of the following
 * Open Source licenses:
 *
 * The GNU General Public License, version 2, or any later version, as
 * published by the Free Software Foundation
 * (http://www.fsf.org/copyleft/gpl.html);
 *
 *  or
 *
 * The Semiotek Public License (http://webmacro.org/LICENSE.)
 *
 * This software is provided "as is", with NO WARRANTY, not even the
 * implied warranties of fitness to purpose, or merchantability. You
 * assume all risks and liabilities associated with its use.
 *
 * See www.webmacro.org for more information on the WebMacro project.
 */

package org.webmacro.directive;

import org.webmacro.*;
import org.webmacro.engine.BuildContext;
import org.webmacro.engine.BuildException;
import org.webmacro.engine.StringTemplate;
import org.webmacro.engine.Variable;
import org.webmacro.servlet.TextTool;

import java.io.IOException;
import java.util.Hashtable;

/*
 * 
 * @author Keats Kirsch
 * Set properties on an object using Java properties file type syntax
 *
 */
public class PropertiesDirective extends Directive
{

   private static final String DEFAULT_CLASS_NAME = "java.util.Hashtable";
   private static final int PROPS_TARGET = 1;
   private static final int PROPS_CLASS = 2;
   private static final int PROPS_CLASSNAME = 3;
   private static final int PROPS_RESULT = 4;

   private Variable target;
   private Object result;
   private String _className;
   //private boolean asMacro = false;

   private static final ArgDescriptor[] myArgs =
      new ArgDescriptor[] {
         new LValueArg(PROPS_TARGET),
         new OptionalGroup(3),
         new KeywordArg(PROPS_CLASS, "class"),
         new AssignmentArg(),
         new QuotedStringArg(PROPS_CLASSNAME),
         new BlockArg(PROPS_RESULT)};

   private static final DirectiveDescriptor myDescr =
      new DirectiveDescriptor("properties", null, myArgs, null);

   public static DirectiveDescriptor getDescriptor()
   {
      return myDescr;
   }

   public PropertiesDirective()
   {
   }

   public Object build(DirectiveBuilder builder, BuildContext bc)
      throws BuildException
   {
      try
      {
         target = (Variable) builder.getArg(PROPS_TARGET, bc);
      }
      catch (ClassCastException e)
      {
         throw new NotVariableBuildException(myDescr.name, e);
      }
      _className = (String) builder.getArg(PROPS_CLASSNAME, bc);
      if (_className == null)
         _className = DEFAULT_CLASS_NAME;
      // 

      //        asMacro = (macroKeyword != null);
      result = builder.getArg(PROPS_RESULT, bc);
      return this;
   }

   public void write(FastWriter out, Context context)
      throws PropertyException, IOException
   {

      try
      {
         if (!context.containsKey(target.getName()))
         {
            // target doesn't exist. Must create.
            try
            {
               Class c = Class.forName(_className);
               Object o = c.newInstance();
               target.setValue(context, o);
            }
            catch (RuntimeException re)
            {
               throw new PropertyException(
                  "Failed to create instance of "
                     + _className
                     + " for the #properties directive. "
                     + re,
                  re);
            }

         }
         //            if (result instanceof Macro && !asMacro)
         //                target.setValue(context, ((Macro) result).evaluate(context));
         //            else
         // evaluate result macro
         Object o = ((Macro) result).evaluate(context);
         String res = (String) ((Macro) result).evaluate(context);
         String[] lines = TextTool.getLines(res);
         String s, prop, val;
         String prefix = "#set $" + target.getVariableName() + ".";
         for (int i = 0; i < lines.length; i++)
         {
            s = lines[i].trim();
            if (s.length() > 0 && !s.startsWith("#"))
            {
               for (int j = 0; j < s.length(); j++)
               {
                  char ch = s.charAt(j);
                  if (ch == ':' || ch == '=')
                  {
                     prop = s.substring(0, j).trim();
                     //TODO check if valid property name?
                     val = s.substring(j + 1).trim();
                     // convert to WM syntax and evaluate
                     // TODO determine if quotes are needed, i.e., is it a number?
                     if (val.length() > 0)
                     {
                        // try first as a string
                        s = prefix + prop + "=\"" + val + "\"";
                        try
                        {
                           new StringTemplate(
                              context.getBroker(),
                              s).evaluateAsString(
                              context);
                        }
                        catch (WebMacroException wme)
                        {
                           // try again without quotes.
									s = prefix + prop + "=" + val;
									try
									{
										new StringTemplate(context.getBroker(),s)
										.evaluateAsString(context);
									}
									catch (WebMacroException wme2)
									{
										// TODO utilize the EEH from the context?
										throw new PropertyException("Failed to set property \"" 
										+ prop + "\" to value \"" + val + "\" on variable \""
										+ target.getVariableName() + "\" of type " 
										+ target.getClass().getName(),
										wme2);
									}
                        }
                        break;
                     }
                  }
               }
            }
            //target.setValue(context, result);
            // parse the 
         }
      }
      catch (PropertyException e)
      {
         throw e;
      }
      catch (Exception e)
      {
         String errorText = "#properties: Unable to set " + target;
         writeWarning(errorText, context, out);
      }
   }

   public void accept(TemplateVisitor v)
   {
      v.beginDirective(myDescr.name);
      v.visitDirectiveArg("PropertiesClassKeyword", "class");
      v.visitDirectiveArg("PropertiesClassName", _className);
      v.visitDirectiveArg("PropertiesTarget", target);
      v.visitDirectiveArg("PropertiesValue", result);
      v.endDirective();
   }

}