package org.jboss.seam.excel.jxl;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;

import jxl.HeaderFooter;
import jxl.HeaderFooter.Contents;
import jxl.biff.DisplayFormat;
import jxl.biff.FontRecord;
import jxl.format.Alignment;
import jxl.format.Border;
import jxl.format.BorderLineStyle;
import jxl.format.Colour;
import jxl.format.Orientation;
import jxl.format.PageOrientation;
import jxl.format.PaperSize;
import jxl.format.Pattern;
import jxl.format.ScriptStyle;
import jxl.format.UnderlineStyle;
import jxl.format.VerticalAlignment;
import jxl.write.WritableFont;
import jxl.write.WriteException;

import org.jboss.seam.core.Interpolator;
import org.jboss.seam.excel.ExcelWorkbookException;
import org.jboss.seam.excel.css.CellStyle;
import org.jboss.seam.excel.ui.ExcelComponent;
import org.jboss.seam.log.Log;
import org.jboss.seam.log.Logging;

/**
 * Factory for creating JExcelAPI objects
 * 
 * @author karlsnic
 */
public class JXLFactory
{
   // Class names for JExcelAPI objects
   private static final String DATEFORMATS_CLASSNAME = "jxl.write.DateFormats";
   private static final String NUMBERFORMATS_CLASSNAME = "jxl.write.NumberFormats";
   private static final String ALIGNMENT_CLASS_NAME = "jxl.format.Alignment";
   private static final String ORIENTATION_CLASS_NAME = "jxl.format.Orientation";
   private static final String VERTICAL_ALIGNMENT_CLASS_NAME = "jxl.format.VerticalAlignment";
   private static final String COLOR_CLASS_NAME = "jxl.format.Colour";
   private static final String BORDER_CLASS_NAME = "jxl.format.Border";
   private static final String BORDER_LINE_STYLE_CLASS_NAME = "jxl.format.BorderLineStyle";
   private static final String PATTERN_CLASS_NAME = "jxl.format.Pattern";
   private static final String PAGE_ORIENTATION_CLASS_NAME = "jxl.format.PageOrientation";
   private static final String PAPER_SIZE_CLASS_NAME = "jxl.format.PaperSize";
   private static final String SCRIPT_STYLE_CLASS_NAME = "jxl.format.ScriptStyle";
   private static final String UNDERLINE_STYLE_CLASS_NAME = "jxl.format.UnderlineStyle";

   private static final String HEADERFOOTER_COMMAND_MARKER = "#";
   private static final String HEADERFOOTER_PAIR_DELIMITER = "=";

   private static final String HF_CMD_FONT_SIZE = "font_size";
   private static final String HF_CMD_FONT_NAME = "font_name";
   private static final String HF_CMD_UNDERLINE = "underline";
   private static final String HF_CMD_SUPERSCRIPT = "superscript";
   private static final String HF_CMD_SUBSCRIPT = "subscript";
   private static final String HF_CMD_STRIKETHROUGH = "strikethrough";
   private static final String HF_CMD_SHADOW = "shadow";
   private static final String HF_CMD_OUTLINE = "outline";
   private static final String HF_CMD_ITALICS = "italics";
   private static final String HF_CMD_DOUBLE_UNDERLINE = "double_underline";
   private static final String HF_CMD_BOLD = "bold";
   private static final String HF_CMD_WORKSHEET_NAME = "worksheet_name";
   private static final String HF_CMD_WORKBOOK_NAME = "workbook_name";
   private static final String HF_CMD_TOTAL_PAGES = "total_pages";
   private static final String HF_CMD_TIME = "time";
   private static final String HF_CMD_PAGE_NUMBER = "page_number";

   private static final Log log = Logging.getLog(JXLFactory.class);

   /**
    * Checks if text is a border line style
    * 
    * @param text The text to check
    * @return True if border line style, false otherwise
    */
   public static boolean isBorderLineStyle(String text)
   {
      return getValidContants(BORDER_LINE_STYLE_CLASS_NAME).contains(text.toLowerCase());
   }

   /**
    * Checks if text is a pattern
    * 
    * @param text The text to check
    * @return True if pattern, false otherwise
    */
   public static boolean isPattern(String text)
   {
      return getValidContants(PATTERN_CLASS_NAME).contains(text.toLowerCase());
   }

   /**
    * Checks if text is a color
    * 
    * @param text The text to check
    * @return True if color, false otherwise
    */
   public static boolean isColor(String text)
   {
      return getValidContants(COLOR_CLASS_NAME).contains(text.toLowerCase());
   }

   /**
    * Checks if text is an alignment
    * 
    * @param text The text to check
    * @return True if alignment, false otherwise
    */
   public static boolean isAlignment(String text)
   {
      return getValidContants(ALIGNMENT_CLASS_NAME).contains(text.toLowerCase());
   }

   /**
    * Checks if text is an orientation
    * 
    * @param text The text to check
    * @return True if orientation, false otherwise
    */
   public static boolean isOrientation(String text)
   {
      return getValidContants(ORIENTATION_CLASS_NAME).contains(text.toLowerCase());
   }

   /**
    * Checks if text is an vertical alignment
    * 
    * @param text The text to check
    * @return True if vertical alignment, false otherwise
    */
   public static boolean isVerticalAlignment(String text)
   {
      return getValidContants(VERTICAL_ALIGNMENT_CLASS_NAME).contains(text.toLowerCase());
   }

   /**
    * Checks if text is an underline style
    * 
    * @param text The text to check
    * @return True if underline style, false otherwise
    */
   public static boolean isUnderlineStyle(String text)
   {
      return getValidContants(UNDERLINE_STYLE_CLASS_NAME).contains(text.toLowerCase());
   }

   /**
    * Checks if text is a script style
    * 
    * @param text The text to check
    * @return True if script style, false otherwise
    */
   public static boolean isScriptStyle(String text)
   {
      return getValidContants(SCRIPT_STYLE_CLASS_NAME).contains(text.toLowerCase());
   }

   /**
    * Gets a list of constants from a class
    * 
    * @param className The class to examine
    * @return A list of constants
    */
   private static List<String> getValidContants(String className)
   {
      List<String> constants = new ArrayList<String>();

      if (log.isTraceEnabled())
      {
         log.trace("Getting valid constants from #0", className);
      }
      Class clazz = null;
      try
      {
         clazz = Class.forName(className);
      }
      catch (ClassNotFoundException e)
      {
         throw new ExcelWorkbookException("Could not find class while getting valid constants", e);
      }
      // Loop through the fields
      for (Field field : clazz.getFields())
      {
         int modifiers = field.getModifiers();
         // Append to list if it's public and static (as most our constants are)
         if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers))
         {
            constants.add(field.getName().toLowerCase());
         }
      }
      return constants;
   }

   /**
    * Gets a suggestion string of available constants from a class.
    * 
    * @param className The class to examine
    * @return The suggestion string
    */
   public static String getValidConstantsSuggestion(String className)
   {
      List<String> constants = getValidContants(className);
      StringBuffer buffer = new StringBuffer();
      int i = 0;
      // Loop through the fields
      for (String field : constants)
      {
         buffer.append(i++ == 0 ? field : ", " + field);
      }
      return Interpolator.instance().interpolate("[#0]", buffer.toString());
   }

   /**
    * Gets a constant from a class
    * 
    * @param className The class name to examine
    * @param fieldName The field to read
    * @return The constant
    * @throws NoSuchFieldException If the field is not available
    */
   private static Object getConstant(String className, String fieldName) throws NoSuchFieldException
   {
      if (log.isTraceEnabled())
      {
         log.trace("Looking for constant #0 in class #1", fieldName.toUpperCase(), className);
      }
      try
      {
         return Class.forName(className).getField(fieldName.toUpperCase()).get(null);
      }
      catch (NoSuchFieldException e)
      {
         throw e;
      }
      catch (Exception e)
      {
         throw new ExcelWorkbookException(Interpolator.instance().interpolate("Could not read field #0 from class #1", fieldName, className), e);
      }
   }

   /**
    * Creates a JExcelAPI representation of an alignment
    * 
    * @param alignment The requested alignment
    * @return The alignment representation
    * @see <a
    *      href="http://jexcelapi.sourceforge.net/resources/javadocs/2_6/docs/jxl/format/Alignment.html">Alignment</a>
    */
   public static Alignment createAlignment(String alignment)
   {
      if (log.isTraceEnabled())
      {
         log.trace("Creating alignment for #0", alignment);
      }
      try
      {
         return alignment == null ? Alignment.LEFT : (Alignment) getConstant(ALIGNMENT_CLASS_NAME, alignment.toUpperCase());
      }
      catch (NoSuchFieldException e)
      {
         String message = Interpolator.instance().interpolate("Alignment {0} not supported, try {1}", alignment, getValidConstantsSuggestion(ALIGNMENT_CLASS_NAME));
         throw new ExcelWorkbookException(message, e);
      }
   }

   /**
    * Creates a JExcelAPI representation of an script style
    * 
    * @param mask The requested script style
    * @return The script style representation
    * @see <a
    *      href="http://jexcelapi.sourceforge.net/resources/javadocs/2_6/docs/jxl/format/ScriptStyle.html">ScriptStyle</a>
    */
   private static ScriptStyle createScriptStyle(String scriptStyle)
   {
      if (log.isTraceEnabled())
      {
         log.trace("Creating script style for #0", scriptStyle);
      }
      try
      {
         return scriptStyle == null ? ScriptStyle.NORMAL_SCRIPT : (ScriptStyle) getConstant(SCRIPT_STYLE_CLASS_NAME, scriptStyle.toUpperCase());
      }
      catch (NoSuchFieldException e)
      {
         String message = Interpolator.instance().interpolate("Script style {0} not supported, try {1}", scriptStyle, getValidConstantsSuggestion(SCRIPT_STYLE_CLASS_NAME));
         throw new ExcelWorkbookException(message, e);
      }
   }

   /**
    * Creates a JExcelAPI representation of an underline style
    * 
    * @param mask The requested underline style
    * @return The underline style representation
    * @see <a
    *      href="http://jexcelapi.sourceforge.net/resources/javadocs/2_6/docs/jxl/format/UnderlineStyle.html">UnderlineStyle</a>
    */
   private static UnderlineStyle createUnderlineStyle(String underlineStyle)
   {
      if (log.isTraceEnabled())
      {
         log.trace("Creating underline style for #0", underlineStyle);
      }
      try
      {
         return underlineStyle == null ? UnderlineStyle.NO_UNDERLINE : (UnderlineStyle) getConstant(UNDERLINE_STYLE_CLASS_NAME, underlineStyle.toUpperCase());
      }
      catch (NoSuchFieldException e)
      {
         String message = Interpolator.instance().interpolate("Underline style {0} not supported, try {1}", underlineStyle, getValidConstantsSuggestion(UNDERLINE_STYLE_CLASS_NAME));
         throw new ExcelWorkbookException(message, e);
      }
   }

   /**
    * Creates a JExcelAPI representation of a font
    * 
    * @param fontspecs The font specifications
    * @return The font
    * @see <a
    *      href="http://jexcelapi.sourceforge.net/resources/javadocs/2_6/docs/jxl/write/WritableFont.html">WritableFont</a>
    */
   public static FontRecord createFont(CellStyle.Font fontspecs) throws WriteException
   {
      WritableFont font = null;
      if (fontspecs.family != null)
      {
         font = new WritableFont(WritableFont.createFont(fontspecs.family));
      }
      else
      {
         font = new WritableFont(WritableFont.ARIAL);
      }
      if (fontspecs.pointSize != null)
      {
         font.setPointSize(fontspecs.pointSize);
      }
      if (fontspecs.color != null)
      {
         font.setColour(createColor(fontspecs.color));
      }
      if (fontspecs.bold != null)
      {
         font.setBoldStyle(fontspecs.bold ? WritableFont.BOLD : WritableFont.NO_BOLD);
      }
      if (fontspecs.italic != null)
      {
         font.setItalic(fontspecs.italic);
      }
      if (fontspecs.struckOut != null)
      {
         font.setStruckout(fontspecs.struckOut);
      }
      if (fontspecs.scriptStyle != null)
      {
         font.setScriptStyle(createScriptStyle(fontspecs.scriptStyle));
      }
      if (fontspecs.underlineStyle != null)
      {
         font.setUnderlineStyle(createUnderlineStyle(fontspecs.underlineStyle));
      }
      return font;
   }

   /**
    * Creates a JExcelAPI number display format
    * 
    * @param formatMask The format mask
    * @return The display format
    * @see <a
    *      href="http://jexcelapi.sourceforge.net/resources/javadocs/2_6/docs/jxl/write/NumberFormat.html">NumberFormat</a>
    */
   public static DisplayFormat createNumberFormat(String formatMask)
   {
      if (log.isTraceEnabled())
      {
         log.trace("Creating number format for mask #0", formatMask);
      }
      try
      {
         return (DisplayFormat) getConstant(NUMBERFORMATS_CLASSNAME, formatMask);
      }
      catch (NoSuchFieldException e)
      {
         // Look! An empty catch block! But this one is documented. We are using
         // this to see if there is a constant
         // defines for this in the class
         return null;
      }
   }

   /**
    * Creates a JExcelAPI date display format
    * 
    * @param mask The format mask
    * @return The display format
    * @see <a
    *      href="http://jexcelapi.sourceforge.net/resources/javadocs/2_6/docs/jxl/write/DateFormat.html">DateFormat</a>
    */
   public static DisplayFormat createDateFormat(String mask)
   {
      if (log.isTraceEnabled())
      {
         log.trace("Creating date format for mask #0", mask);
      }
      try
      {
         return (DisplayFormat) getConstant(DATEFORMATS_CLASSNAME, mask.toUpperCase());
      }
      catch (NoSuchFieldException e)
      {
         // Look! An empty catch block! But this one is documented. We are using
         // this to see if there is a constant
         // defines for this in the class
         return null;
      }
   }

   /**
    * Creates a JExcelAPI representation of an color
    * 
    * @param color The requested color
    * @return The color representation
    * @see <a
    *      href="http://jexcelapi.sourceforge.net/resources/javadocs/2_6/docs/jxl/format/Color.html">Color</a>
    */
   public static Colour createColor(String color)
   {
      if (log.isTraceEnabled())
      {
         log.trace("Creating color for #0", color);
      }
      // Workaround for the feature that black is... well not always black in
      // Excel (ref: Andy Khan on yahoo groups)
      if (color.equalsIgnoreCase("black"))
      {
         color = "palette_black";
      }
      try
      {
         return color == null ? Colour.AUTOMATIC : (Colour) getConstant(COLOR_CLASS_NAME, color.toUpperCase());
      }
      catch (NoSuchFieldException e)
      {
         String message = Interpolator.instance().interpolate("Color {0} not supported, try {1}", color, getValidConstantsSuggestion(COLOR_CLASS_NAME));
         throw new ExcelWorkbookException(message, e);
      }
   }

   /**
    * Creates a JExcelAPI representation of an orientation
    * 
    * @param orientation The requested orientation
    * @return The orientation representation
    * @see <a
    *      href="http://jexcelapi.sourceforge.net/resources/javadocs/2_6/docs/jxl/format/Orientation.html">Orientation</a>
    */
   public static Orientation createOrientation(String orientation)
   {
      if (log.isTraceEnabled())
      {
         log.trace("Creating orientation for #0", orientation);
      }
      try
      {
         return orientation == null ? Orientation.HORIZONTAL : (Orientation) getConstant(ORIENTATION_CLASS_NAME, orientation.toUpperCase());
      }
      catch (NoSuchFieldException e)
      {
         String message = Interpolator.instance().interpolate("Orientation {0} not supported, try {1}", orientation, getValidConstantsSuggestion(ORIENTATION_CLASS_NAME));
         throw new ExcelWorkbookException(message, e);
      }
   }

   /**
    * Creates a JExcelAPI representation of a vertical alignment
    * 
    * @param verticalAlignment The requested alignment
    * @return The alignment representation
    * @see <a
    *      href="http://jexcelapi.sourceforge.net/resources/javadocs/2_6/docs/jxl/format/VerticalAlignment.html">VerticalAlignment</a>
    */
   public static VerticalAlignment createVerticalAlignment(String verticalAlignment)
   {
      if (log.isTraceEnabled())
      {
         log.trace("Creating verical alignment for #0", verticalAlignment);
      }
      try
      {
         return verticalAlignment == null ? VerticalAlignment.BOTTOM : (VerticalAlignment) getConstant(VERTICAL_ALIGNMENT_CLASS_NAME, verticalAlignment.toUpperCase());
      }
      catch (NoSuchFieldException e)
      {
         String message = Interpolator.instance().interpolate("Verical alignment {0} not supported, try {1}", verticalAlignment, getValidConstantsSuggestion(VERTICAL_ALIGNMENT_CLASS_NAME));
         throw new ExcelWorkbookException(message, e);
      }
   }

   /**
    * Creates a JExcelAPI representation of a border
    * 
    * @param border The requested border
    * @return border representation
    * @see <a
    *      href="http://jexcelapi.sourceforge.net/resources/javadocs/2_6/docs/jxl/format/Border.html">Border</a>
    */
   public static Border createBorder(String border)
   {
      if (log.isTraceEnabled())
      {
         log.trace("Creating border for #0", border);
      }
      try
      {
         return border == null ? Border.ALL : (Border) getConstant(BORDER_CLASS_NAME, border.toUpperCase());
      }
      catch (NoSuchFieldException e)
      {
         String message = Interpolator.instance().interpolate("Border {0} not supported, try {1}", border, getValidConstantsSuggestion(BORDER_CLASS_NAME));
         throw new ExcelWorkbookException(message, e);
      }
   }

   /**
    * Creates a JExcelAPI representation of a border line style
    * 
    * @param borderLineStyle The requested border line style
    * @return The border line style representation
    * @see <a
    *      href="http://jexcelapi.sourceforge.net/resources/javadocs/2_6/docs/jxl/format/BorderLineStyle.html">BorderLineStyle</a>
    */
   public static BorderLineStyle createLineStyle(String borderLineStyle)
   {
      if (log.isTraceEnabled())
      {
         log.trace("Creating border line style for #0", borderLineStyle);
      }
      try
      {
         return borderLineStyle == null ? BorderLineStyle.NONE : (BorderLineStyle) getConstant(BORDER_LINE_STYLE_CLASS_NAME, borderLineStyle.toUpperCase());
      }
      catch (NoSuchFieldException e)
      {
         String message = Interpolator.instance().interpolate("Border line style {0} not supported, try {1}", borderLineStyle, getValidConstantsSuggestion(BORDER_LINE_STYLE_CLASS_NAME));
         throw new ExcelWorkbookException(message, e);
      }
   }

   /**
    * Creates a JExcelAPI representation of a pattern
    * 
    * @param pattern The requested pattern
    * @return The pattern representation
    * @see <a
    *      href="http://jexcelapi.sourceforge.net/resources/javadocs/2_6/docs/jxl/format/Pattern.html">Pattern</a>
    */
   public static Pattern createPattern(String pattern)
   {
      if (log.isTraceEnabled())
      {
         log.trace("Creating pattern for #0", pattern);
      }
      try
      {
         return pattern == null ? Pattern.SOLID : (Pattern) getConstant(PATTERN_CLASS_NAME, pattern.toUpperCase());
      }
      catch (NoSuchFieldException e)
      {
         String message = Interpolator.instance().interpolate("Pattern {0} not supported, try {1}", pattern, getValidConstantsSuggestion(PATTERN_CLASS_NAME));
         throw new ExcelWorkbookException(message, e);
      }
   }

   /**
    * Creates a JExcelAPI representation of a page orientation
    * 
    * @param orientation The type of orientation to create
    * @return The page orientation representation
    */
   public static PageOrientation createPageOrientation(String orientation)
   {
      if (log.isTraceEnabled())
      {
         log.trace("Creating page orientation for #0", orientation);
      }
      try
      {
         return orientation == null ? PageOrientation.LANDSCAPE : (PageOrientation) getConstant(PAGE_ORIENTATION_CLASS_NAME, orientation.toUpperCase());
      }
      catch (NoSuchFieldException e)
      {
         String message = Interpolator.instance().interpolate("Page orientation {0} not supported, try {1}", orientation, getValidConstantsSuggestion(PAGE_ORIENTATION_CLASS_NAME));
         throw new ExcelWorkbookException(message, e);
      }
   }

   /**
    * Creates a JExcelAPI representation of a paper size
    * 
    * @param paperSize The type of paper size to create
    * @return The paper size representation
    */
   public static PaperSize createPaperSize(String paperSize)
   {
      if (log.isTraceEnabled())
      {
         log.trace("Creating paper size for #0", paperSize);
      }
      try
      {
         return paperSize == null ? PaperSize.A4 : (PaperSize) getConstant(PAPER_SIZE_CLASS_NAME, paperSize.toUpperCase());
      }
      catch (NoSuchFieldException e)
      {
         String message = Interpolator.instance().interpolate("Page size {0} not supported, try {1}", paperSize, getValidConstantsSuggestion(PAPER_SIZE_CLASS_NAME));
         throw new ExcelWorkbookException(message, e);
      }
   }

   /**
    * Creates a JExcelAPI header or footer representation. Processes the left,
    * center and right facets using a helper method
    * 
    * @param uiHeaderFooter The UI header or footer to interpret
    * @param headerFooter The JExcelAPI header or footer representation to add
    *           to
    * @return The JExcelAPI header or footer representation
    */
   public static HeaderFooter createHeaderFooter(UIComponent uiHeaderFooter, HeaderFooter headerFooter)
   {
      if (log.isTraceEnabled())
      {
         log.trace("Processing header/footer #0", uiHeaderFooter);
      }
      processHeaderFooterFacet(headerFooter.getLeft(), uiHeaderFooter.getFacet("left"));
      processHeaderFooterFacet(headerFooter.getCentre(), uiHeaderFooter.getFacet("center"));
      processHeaderFooterFacet(headerFooter.getRight(), uiHeaderFooter.getFacet("right"));
      return headerFooter;
   }

   /**
    * Processes a header or footer facet. A header or footer facet in JExcelAPI
    * is split into three parts, left, center and right and the UI
    * representation has facets with the same namings. Gets the requested facet
    * from the UI component and calls helper methods for processing the header
    * commands in sequence
    * 
    * @param headerFooter The JExcelAPI header or footer facet to process
    * @param facetName The name of the facet to process (left, center, right)
    * @param uiHeaderFooter The UI representation to interpret
    */
   private static void processHeaderFooterFacet(HeaderFooter.Contents contents, UIComponent facet)
   {
      if (log.isTraceEnabled())
      {
         log.trace("Processing facet #0 of header/footer #1", facet, contents);
      }
      // No facet found
      if (facet == null)
      {
         return;
      }
      String facetContent = null;
      try
      {
         facetContent = ExcelComponent.cmp2String(FacesContext.getCurrentInstance(), facet);
      }
      catch (IOException e)
      {
         throw new ExcelWorkbookException("Could not get content from header facet", e);
      }
      if (facetContent == null)
      {
         return;
      }
      facetContent = facetContent.trim();
      int firstHash;
      int secondHash;
      String command;
      String pre;
      while (!"".equals(facetContent))
      {
         firstHash = -1;
         secondHash = -1;
         firstHash = facetContent.indexOf(HEADERFOOTER_COMMAND_MARKER);
         if (firstHash >= 0)
         {
            secondHash = facetContent.indexOf(HEADERFOOTER_COMMAND_MARKER, firstHash + 1);
         }
         if (firstHash >= 0 && secondHash >= 0 && firstHash != secondHash)
         {
            pre = facetContent.substring(0, firstHash);
            if (!"".equals(pre))
            {
               contents.append(pre);
            }
            command = facetContent.substring(firstHash + 1, secondHash);
            processCommand(contents, command);
            facetContent = facetContent.substring(secondHash + 1);
         }
         else
         {
            contents.append(facetContent);
            facetContent = "";
         }
      }
   }

   /**
    * Processes a header or footer command, adding itself to the contents
    * 
    * @param contents The target contents
    * @param command The command to execute
    */
   private static void processCommand(Contents contents, String command)
   {
      command = command.toLowerCase();
      if (command.startsWith("date"))
      {
         contents.appendDate();
      }
      else if (command.startsWith(HF_CMD_PAGE_NUMBER))
      {
         contents.appendPageNumber();
      }
      else if (command.startsWith(HF_CMD_TIME))
      {
         contents.appendTime();
      }
      else if (command.startsWith(HF_CMD_TOTAL_PAGES))
      {
         contents.appendTotalPages();
      }
      else if (command.startsWith(HF_CMD_WORKBOOK_NAME))
      {
         contents.appendWorkbookName();
      }
      else if (command.startsWith(HF_CMD_WORKSHEET_NAME))
      {
         contents.appendWorkSheetName();
      }
      else if (command.startsWith(HF_CMD_BOLD))
      {
         contents.toggleBold();
      }
      else if (command.startsWith(HF_CMD_DOUBLE_UNDERLINE))
      {
         contents.toggleDoubleUnderline();
      }
      else if (command.startsWith(HF_CMD_ITALICS))
      {
         contents.toggleItalics();
      }
      else if (command.startsWith(HF_CMD_OUTLINE))
      {
         contents.toggleOutline();
      }
      else if (command.startsWith(HF_CMD_SHADOW))
      {
         contents.toggleShadow();
      }
      else if (command.startsWith(HF_CMD_STRIKETHROUGH))
      {
         contents.toggleStrikethrough();
      }
      else if (command.startsWith(HF_CMD_SUBSCRIPT))
      {
         contents.toggleSubScript();
      }
      else if (command.startsWith(HF_CMD_SUPERSCRIPT))
      {
         contents.toggleSuperScript();
      }
      else if (command.startsWith(HF_CMD_UNDERLINE))
      {
         contents.toggleUnderline();
      }
      else if (command.startsWith(HF_CMD_FONT_NAME))
      {
         String[] parts = command.split(HEADERFOOTER_PAIR_DELIMITER);
         if (parts.length != 2)
         {
            log.warn("Header/Footer font name error in #0", command);
         }
         contents.setFontName(parts[1].trim());
      }
      else if (command.startsWith(HF_CMD_FONT_SIZE))
      {
         String[] parts = command.split(HEADERFOOTER_PAIR_DELIMITER);
         if (parts.length != 2)
         {
            log.warn("Header/Footer font size error in #0", command);
         }
         contents.setFontSize(Integer.parseInt(parts[1].trim()));
      }
   }

}
