/**
 * GUI Commands
 * Copyright 2005 Andrew Pietsch
 *
 * 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.
 *
 * $Id: AbstractBuilder.java,v 1.7 2006/02/26 00:59:04 pietschy Exp $
 */
package org.pietschy.command;

import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import java.util.ResourceBundle;

/**
 * Provides common methods required by all builders.
 *
 * @author andrewp
 * @version $Revision: 1.7 $
 */
public class
AbstractBuilder
{
   private static final String _ID_ = "$Id: AbstractBuilder.java,v 1.7 2006/02/26 00:59:04 pietschy Exp $";

   private CommandManager commandManager;

   /**
    * Gets the associated {@link CommandManager} for the builder to use as required.  This will be
    * configured when the builder is installed.  The {@link CommandManager}
    * is typically used to gain access the the class loader and resource bundle.
    *
    * @return the associated {@link CommandManager}.
    * @see CommandManager#getClassLoader
    */
   public CommandManager
   getCommandManager()
   {
      return commandManager;
   }

   /**
    * Sets the associated {@link CommandManager} for the builder to use as required.  This will be
    * configured when the builder is installed.  The {@link CommandManager}
    * is typically used to gain access the the class loader and resource bundle.
    *
    * @param commandManager the associated {@link CommandManager}.
    * @see CommandManager#getClassLoader
    */
   public void
   setCommandManager(CommandManager commandManager)
   {
      this.commandManager = commandManager;
   }

   /**
    * Gets the textural value of an element.  If the element has {@link Names#I18N_ATTRIBUTE} then
    * this method will attempt to load the value from the resource bundle.
    *
    * @param element the element whose text value is to be extracted.
    * @return the text value of the element
    */
   protected String
   getElementText(Element element)
   {
      Attr i18nAttribute = element.getAttributeNode(Names.I18N_ATTRIBUTE);
      if (i18nAttribute != null)
      {
         String key = i18nAttribute.getValue();

         ResourceBundle bundle = getCommandManager().getResourceBundle();

         if (bundle == null)
            throw new IllegalStateException("resource bundle is null, unable to resolve resource '" + key + "' on " + getElementPath(element));

         return bundle.getString(key);
      }

      return Util.getElementText(element);

   }

   /**
    * Gets an attribute from an element.  If the attribute is prefixed with {@link Names#I18N_ATTRIBUTE_PREFIX} then
    * this method will attempt to load the attribute from the resource bundle.  This method returns <tt>null</tt> if
    * the the requested attribute isn't present.
    *
    * @param element       the element constaining the attribute
    * @param attributeName the attribute name
    * @return the attribute value, or <tt>null</tt> if the attribute doesn't exist.
    */
   protected String
   getAttribute(Element element, String attributeName)
   {
      Attr attribute = element.getAttributeNode(attributeName);
      if (attribute == null)
         return null;

      String value = attribute.getValue();
      if (value.startsWith(Names.I18N_ATTRIBUTE_PREFIX))
      {
         String key = value.substring(Names.I18N_ATTRIBUTE_PREFIX.length());

         ResourceBundle bundle = getCommandManager().getResourceBundle();

         if (bundle == null)
            throw new IllegalStateException("resource bundle is null, unable to resolve attribute '" + attributeName + "' on " + getElementPath(element));

         value = bundle.getString(key);
      }

      return value;
   }

   /**
    * Checks if the specified element is empty.  Emply elements are those that have no children (not
    * even an empty text node).  A empty node is specified in the form &lt;text/&gt;.  Empty
    * nodes are used to signify that a particular face element is null and that it shouldn't inherit
    * from its parent.
    *
    * @param element the element to check.
    * @return <tt>true</tt> if the element has no children and no attributes, <tt>false</tt> otherwise.
    */
   protected boolean
   isEmptyElement(Element element)
   {
      return (!element.hasChildNodes() && !element.hasAttributes());
   }

   /**
    * Returns a string representing the elements position in the command heirarchy.  This method is only intended
    * to produce debug information.
    *
    * @param element the element to examine.
    * @return a string representing the elements posistion in the configuration heirarchy.
    */
   public String
   getElementPath(Element element)
   {
      // todo (ap): update this to display command ids
//      element.
      return element.getTagName() + " at unknown location";//element.getUniquePath();
   }

   /**
    * Tests if the specified element has been conditionally included.
    * <p>
    * This method will always return <tt>true</tt>
    * if there is no <tt>include-if</tt> element.  If present, this method will evaulate the exprerssion using
    * {@link ConditionEvaluator#evaluate(String)} returning the result.
    *
    * @param e the element that may or may not have a <tt>include-if</tt> attriubte.
    * @return <tt>false</tt> if and only if there is an <tt>include-if</tt> attribute and the
    * expression evaluates to <tt>false</tt>.
    */
   protected boolean
   isIncluded(Element e)
   {

      String expression = getAttribute(e, Names.INCLUDE_IF_ATTRIBUTE);

      if (expression == null)
         return true;

      try
      {
         // if the condition is false, we skip the element
         return getCommandManager().getConditionEvaluator().evaluate(expression);
      }
      catch (EvaluationException ee)
      {
         throw new RuntimeException("Couldn't parse conditon", ee);
      }
   }

   protected void
   populateProperties(Command command, Element commandRoot)
   {
      NodeList elements = commandRoot.getElementsByTagName(Names.PROPERTY_ELEMENT);
      for (int i = 0; i < elements.getLength(); i++)
      {
         Element e = (Element) elements.item(i);
         command.putProperty(getAttribute(e, Names.NAME_ATTRIBUTE), getElementText(e).trim());
      }
   }

   
}
