/* ======================================================================
   Parts Copyright 2006 University of Leeds, Oxford University, University of the Highlands and Islands.

   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.

====================================================================== */

package org.bodington.servlet.facilities;

import java.awt.Color;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Hashtable;
import java.util.Properties;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.bodington.database.PrimaryKey;
import org.bodington.server.BuildingServer;
import org.bodington.server.BuildingServerException;
import org.bodington.server.BuildingSession;
import org.bodington.server.BuildingSessionManagerImpl;
import org.bodington.server.NavigationSession;
import org.bodington.server.resources.Resource;
import org.bodington.servlet.Request;
import org.bodington.servlet.Response;
import org.bodington.servlet.ServletUtils;
import org.bodington.servlet.template.Template;
import org.bodington.util.ColourPreferenceMapper;

/**
 * The class holds most of the code for dealing with generation of StyleSheets.
 * The code was originally in {@link Facility} but was refactored out to reduce
 * the size of Facility. 
 * @author buckett
 */
public class StyleSheets
{

    private static Logger log = Logger.getLogger(StyleSheets.class);
    private java.util.Random random = new java.util.Random();
    Hashtable author_style_sheets = new Hashtable();

    static final int FOREGROUND = 0;
    static final int FOREGROUND_EM = 1;
    static final int FOREGROUND_VSTRONG = 2;
    static final int FOREGROUND_LINK = 3;
    static final int FOREGROUND_VISITED = 4;
    static final int FOREGROUND_ACTIVE = 5;
    static final int FOREGROUND_HOVER = 6;

    static final int BACKGROUND = 0;
    static final int BACKGROUND_TABLE = 1;
    static final int BACKGROUND_TABLE_EM = 2;
    static final int BACKGROUND_MENU_HEAD = 3;
    static final int BACKGROUND_MENU_1 = 4;
    static final int BACKGROUND_MENU_2 = 5;

    // CSS elements that have background colours indexed with above constants
    // excepting trees
    String[][] css_back_elements = {{"body", ".bs-plain"},
        {".bs-table-opaque", ".bs-logbook-command"},
        {".bs-cell-special", ".bs-table-acl"},
        {".res_tree_header", ".res_tree_big_header"}};
    // CSS elements that have foreground colours
    String[][] css_fore_elements = {
        {"", ".bs-messaging-date"},
        {"em", "strong", ".bs-links-heading", ".bs-messaging-week",
            ".bs-logbook-menu-command", ".bs-logbook-menu-command A",
            ".bs-logbook-menu-section A", ".bs-logbook-menu-section A:visited",
            ".bs-logbook-entry-author", ".bs-logbook-entry-date"},
        {".bs-messaging-day"}, {"a:link"}, {"a:visited"}, {"a:active"},
        {"a:hover"}};
    static final Color[][] def_back_col = {
        {new Color(0xccccc0), new Color(0x9090b0), new Color(0x8080b0), null,
            null, null},
        {new Color(0x007c88), new Color(0x9090b0), new Color(0x8080b0), null,
            null, null}};
    static final Color[][] def_fore_col = {
        {new Color(0x333366), new Color(0xaa6600), new Color(0xaa6600),
            new Color(0x403399), new Color(0x333380), new Color(0x333399),
            new Color(0x8040ff)},
        {new Color(0xffffff), new Color(0xffa401), new Color(0xff0000),
            new Color(0xaaaaff), new Color(0xccccdd), new Color(0x333399),
            new Color(0x8040ff)}};

    /**
     * Create a stylesheet for the current resource.
     */
    private StyleSheetEntry compileAuthorStyleSheet(Request req,
        BuildingSession session, PrimaryKey resource_id)
        throws BuildingServerException
    {
        int i, j, k, l;

        log.debug("Compiling Style Sheet");

        synchronized (author_style_sheets)
        {
            String property;

            String nav_background_image = null;
            property = session.getProperty("style_navigation_background_image");
            if ("none".equals(property))
                nav_background_image = "none";
            else if (property != null)
            {
                nav_background_image = "url('" + req.getContextPath();
                if (!property.startsWith("/")) nav_background_image += "/";
                nav_background_image += property + "')";
            }

            // starting to reform and simplify colour processing
            Color[] nav_foreground_colours = getForegroundColors(session,
                "style_navigation");
            Color[] foreground_colours = getForegroundColors(session, "style");
            Color[] nav_background_colours = getBackgroundColors(session,
                "style_navigation");
            Color[] background_colours = getBackgroundColors(session, "style");
            Color res_menu_border_colour = session.getPropertyColor(
                "style_navigation_menu_border_colour", null);
            boolean tree_lines = "yes".equalsIgnoreCase(session.getProperty(
                "ui_navigation_menu_lines", "yes"));

            // These lines of code are about determining the colour of lines in
            // the background
            // of tree diagrams. The code determined will be used to transform
            // the colour of
            // the GIF files via a query string to the servlet that processed
            // them.
            // Line will be black or white depending on background colour of
            // page
            Color tree_line_colour = null;
            // if background is lighter than mid grey use black lines
            // otherwise use white lines.
            float[] hsb = Color.RGBtoHSB(nav_background_colours[BACKGROUND]
                .getRed(), nav_background_colours[BACKGROUND].getGreen(),
                nav_background_colours[BACKGROUND].getBlue(), null);
            if (hsb[2] < 0.5)
                tree_line_colour = new Color(0xffffff);
            else
                tree_line_colour = new Color(0x000000);
            // not concerned with user preference here.
            ColourPreferenceMapper tree_line_colour_mapper = null;
            String tree_line_path = null;
            if (tree_lines)
            {
                tree_line_colour_mapper = new ColourPreferenceMapper();
                tree_line_colour_mapper.setReferenceColours(
                    nav_background_colours[BACKGROUND], tree_line_colour);
                // next we need to set up a path to a transformed sample tree
                // line
                // GIF file. It is assumed that all GIFs will be in the same
                // place.
                tree_line_path = treeLinePath(req);
            }

            String background_image = null;
            property = session.getProperty("style_background_image");
            if ("none".equals(property))
                background_image = "none";
            else if (property != null)
            {
                background_image = "url('" + req.getContextPath();
                if (!property.startsWith("/")) background_image += "/";
                background_image += property + "')";
            }

            StyleSheetEntry entry = new StyleSheetEntry();
            entry.content = new StringBuffer();

            entry.content
                .append("/* Bodington Virtual Style Sheet - Author's */\r\n\r\n");

            stylesheetEntry(entry.content, "body, .bs-plain");
            stylesheetProperty(entry.content, "color",
                foreground_colours[FOREGROUND]);
            if (background_image != null)
            {
                stylesheetProperty(entry.content, "background-image",
                    background_image);
                stylesheetProperty(entry.content, "background-repeat", "repeat");
            }

            stylesheetProperty(entry.content, "background-color",
                background_colours[BACKGROUND]);
            stylesheetProperty(entry.content, "font-family",
                "Arial Unicode MS, sans-serif");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content,
                ".bodington_navigation_page, .bodington_navigation_page .bs-plain");
            stylesheetProperty(entry.content, "color",
                nav_foreground_colours[FOREGROUND]);
            if (nav_background_image != null)
            {
                stylesheetProperty(entry.content, "background-image",
                    nav_background_image);
                stylesheetProperty(entry.content, "background-repeat", "repeat");
            }

            stylesheetProperty(entry.content, "background-color",
                nav_background_colours[BACKGROUND]);
            stylesheetProperty(entry.content, "font-family",
                "Arial Unicode MS, sans-serif");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-textarea");
            stylesheetProperty(entry.content, "font-size", "100%");
            stylesheetProperty(entry.content, "font-family",
                "Arial Unicode MS, sans-serif");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, "textarea");
            stylesheetProperty(entry.content, "font-size", "100%");
            stylesheetProperty(entry.content, "font-family",
                "Arial Unicode MS, sans-serif");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, "input");
            stylesheetProperty(entry.content, "font-size", "100%");
            stylesheetProperty(entry.content, "font-family",
                "Arial Unicode MS, sans-serif");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, "select");
            stylesheetProperty(entry.content, "font-size", "100%");
            stylesheetProperty(entry.content, "font-family",
                "Arial Unicode MS, sans-serif");
            stylesheetEndEntry(entry.content);

            // It's likely that some block elements have defined absolute sizes
            // in the browser's
            // default style which mean that they are not tied to the font size
            // in the body
            // tag. So the set of likely elements are defined here with relative
            // sizes.
            stylesheetEntry(entry.content, "table");
            stylesheetProperty(entry.content, "font-size", "100%");
            stylesheetEndEntry(entry.content);
            stylesheetEntry(entry.content, "h1");
            stylesheetProperty(entry.content, "font-size", "190%");
            stylesheetEndEntry(entry.content);
            stylesheetEntry(entry.content, "h2");
            stylesheetProperty(entry.content, "font-size", "150%");
            stylesheetEndEntry(entry.content);
            stylesheetEntry(entry.content, "h3");
            stylesheetProperty(entry.content, "font-size", "120%");
            stylesheetEndEntry(entry.content);
            stylesheetEntry(entry.content, "h4");
            stylesheetProperty(entry.content, "font-size", "100%");
            stylesheetEndEntry(entry.content);
            stylesheetEntry(entry.content, "h5");
            stylesheetProperty(entry.content, "font-size", "85%");
            stylesheetEndEntry(entry.content);
            stylesheetEntry(entry.content, "h6");
            stylesheetProperty(entry.content, "font-size", "75%");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bodington_navigation_page a:link");
            stylesheetProperty(entry.content, "color",
                nav_foreground_colours[FOREGROUND_LINK]);
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content,
                ".bodington_navigation_page a:visited");
            stylesheetProperty(entry.content, "color",
                nav_foreground_colours[FOREGROUND_VISITED]);
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content,
                ".bodington_navigation_page a:active");
            stylesheetProperty(entry.content, "color",
                nav_foreground_colours[FOREGROUND_ACTIVE]);
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bodington_navigation_page a:hover");
            stylesheetProperty(entry.content, "color",
                nav_foreground_colours[FOREGROUND_HOVER]);
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, "a:link");
            stylesheetProperty(entry.content, "color",
                foreground_colours[FOREGROUND_LINK]);
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, "a:visited");
            stylesheetProperty(entry.content, "color",
                foreground_colours[FOREGROUND_VISITED]);
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, "a:active");
            stylesheetProperty(entry.content, "color",
                foreground_colours[FOREGROUND_ACTIVE]);
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, "a:hover");
            stylesheetProperty(entry.content, "color",
                foreground_colours[FOREGROUND_HOVER]);
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bodington_navigation_page em");
            stylesheetProperty(entry.content, "color",
                nav_foreground_colours[FOREGROUND_EM]);
            stylesheetProperty(entry.content, "font-style", "normal");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, "em");
            stylesheetProperty(entry.content, "color",
                foreground_colours[FOREGROUND_EM]);
            stylesheetProperty(entry.content, "font-style", "normal");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bodington_navigation_page strong");
            stylesheetProperty(entry.content, "color",
                nav_foreground_colours[FOREGROUND_EM]);
            stylesheetProperty(entry.content, "font-style", "normal");
            stylesheetProperty(entry.content, "font-weight", "bold");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, "strong");
            stylesheetProperty(entry.content, "color",
                foreground_colours[FOREGROUND_EM]);
            stylesheetProperty(entry.content, "font-style", "normal");
            stylesheetProperty(entry.content, "font-weight", "bold");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-title");
            stylesheetProperty(entry.content, "font-weight", "bold");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-table-acl");
            stylesheetProperty(entry.content, "background-color",
                background_colours[BACKGROUND_TABLE]);
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content,
                ".bodington_navigation_page .bs-table-acl");
            stylesheetProperty(entry.content, "background-color",
                nav_background_colours[BACKGROUND_TABLE]);
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-table-opaque");
            stylesheetProperty(entry.content, "background-color",
                background_colours[BACKGROUND_TABLE]);
            stylesheetProperty(entry.content, "border-left", "thin ridge");
            stylesheetProperty(entry.content, "border-top", "thin ridge");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content,
                ".bodington_navigation_page .bs-table-opaque");
            stylesheetProperty(entry.content, "background-color",
                nav_background_colours[BACKGROUND_TABLE]);
            stylesheetProperty(entry.content, "border-left", "thin ridge");
            stylesheetProperty(entry.content, "border-top", "thin ridge");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-cell-special");
            stylesheetProperty(entry.content, "background-color",
                background_colours[BACKGROUND_TABLE_EM]);
            stylesheetProperty(entry.content, "border-right", "thin ridge");
            stylesheetProperty(entry.content, "border-bottom", "thin ridge");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content,
                ".bodington_navigation_page .bs-cell-special");
            stylesheetProperty(entry.content, "background-color",
                nav_background_colours[BACKGROUND_TABLE_EM]);
            stylesheetProperty(entry.content, "border-right", "thin ridge");
            stylesheetProperty(entry.content, "border-bottom", "thin ridge");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-introduction");
            stylesheetProperty(entry.content, "margin", "0.5em");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-links-title");
            stylesheetProperty(entry.content, "font-size", "95%");
            stylesheetProperty(entry.content, "font-weight", "bold");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-links-heading");
            stylesheetProperty(entry.content, "color",
                nav_foreground_colours[FOREGROUND_EM]);
            stylesheetProperty(entry.content, "font-size", "95%");
            stylesheetProperty(entry.content, "font-weight", "bold");
            stylesheetProperty(entry.content, "margin-top", "+25px");
            stylesheetProperty(entry.content, "margin-bottom", "0px");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-links-icon");
            stylesheetProperty(entry.content, "margin-right", "10px");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-links-description");
            stylesheetProperty(entry.content, "font-size", "90%");
            stylesheetProperty(entry.content, "margin-bottom", "1ex");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-links-heading-description");
            stylesheetProperty(entry.content, "margin-left", "+30px");
            stylesheetProperty(entry.content, "margin-top", "0px");
            stylesheetProperty(entry.content, "margin-bottom", "+15px");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-messaging-title");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-messaging-detail");
            stylesheetProperty(entry.content, "padding-top", "0px");
            stylesheetProperty(entry.content, "padding-bottom", "32px");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-messaging-introduction");
            stylesheetProperty(entry.content, "padding-bottom", "+2ex");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-messaging-date");
            stylesheetProperty(entry.content, "color",
                foreground_colours[FOREGROUND]);
            stylesheetProperty(entry.content, "font-style", "normal");
            stylesheetProperty(entry.content, "font-weight", "normal");
            stylesheetProperty(entry.content, "font-size", "80%");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content,
                ".bs-messaging-title .bs-messaging-date");
            stylesheetProperty(entry.content, "color",
                foreground_colours[FOREGROUND]);
            stylesheetProperty(entry.content, "font-style", "normal");
            stylesheetProperty(entry.content, "font-weight", "normal");
            stylesheetProperty(entry.content, "font-size", "50%");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-messaging-day");
            stylesheetProperty(entry.content, "color",
                foreground_colours[FOREGROUND_VSTRONG]);
            stylesheetProperty(entry.content, "font-style", "normal");
            stylesheetProperty(entry.content, "font-weight", "bold");
            stylesheetProperty(entry.content, "font-size", "100%");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content,
                ".bs-messaging-title .bs-messaging-day");
            stylesheetProperty(entry.content, "color",
                foreground_colours[FOREGROUND_VSTRONG]);
            stylesheetProperty(entry.content, "font-style", "normal");
            stylesheetProperty(entry.content, "font-weight", "bold");
            stylesheetProperty(entry.content, "font-size", "80%");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-messaging-week");
            stylesheetProperty(entry.content, "color",
                foreground_colours[FOREGROUND_EM]);
            stylesheetProperty(entry.content, "font-style", "normal");
            stylesheetProperty(entry.content, "font-weight", "normal");
            stylesheetProperty(entry.content, "font-size", "100%");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content,
                ".bs-messaging-title .bs-messaging-week");
            stylesheetProperty(entry.content, "color",
                foreground_colours[FOREGROUND_EM]);
            stylesheetProperty(entry.content, "font-style", "normal");
            stylesheetProperty(entry.content, "font-weight", "normal");
            stylesheetProperty(entry.content, "font-size", "80%");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-messaging-author");
            stylesheetProperty(entry.content, "color",
                foreground_colours[FOREGROUND_EM]);
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content,
                ".bs-messaging-title .bs-messaging-author");
            stylesheetProperty(entry.content, "color",
                foreground_colours[FOREGROUND_EM]);
            stylesheetProperty(entry.content, "font-size", "80%");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-messaging-number");
            stylesheetProperty(entry.content, "font-size", "50%");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-messaging-commands");
            stylesheetProperty(entry.content, "padding-top", "4px");
            stylesheetProperty(entry.content, "padding-bottom", "4px");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-messaging-command");
            stylesheetProperty(entry.content, "border", "thin white");
            stylesheetProperty(entry.content, "padding-left", "0px");
            stylesheetProperty(entry.content, "padding-right", "32px");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-messaging-sample");
            stylesheetProperty(entry.content, "padding-top", "4px");
            stylesheetProperty(entry.content, "padding-bottom", "4px");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-messaging-sample-text");
            stylesheetProperty(entry.content, "font-style", "italic");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-mcq-marks");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-logbook-commands");
            stylesheetProperty(entry.content, "text-align", "center");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-logbook-command");
            stylesheetProperty(entry.content, "background-color",
                background_colours[BACKGROUND_TABLE]);
            stylesheetProperty(entry.content, "margin-left", "10px");
            stylesheetProperty(entry.content, "margin-right", "10px");
            stylesheetProperty(entry.content, "padding", "4px");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-logbook-command-disabled");
            stylesheetProperty(entry.content, "color", "#202020");
            stylesheetProperty(entry.content, "background-color", "#a8a8a8");
            stylesheetProperty(entry.content, "margin-left", "10px");
            stylesheetProperty(entry.content, "margin-right", "10px");
            stylesheetProperty(entry.content, "padding", "4px");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-logbook-menu");
            stylesheetProperty(entry.content, "border", "thin dashed white");
            stylesheetProperty(entry.content, "margin", "16px");
            stylesheetProperty(entry.content, "padding-top", "4px");
            stylesheetProperty(entry.content, "padding-bottom", "15px");
            stylesheetProperty(entry.content, "padding-left", "4px");
            stylesheetProperty(entry.content, "padding-right", "4px");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-logbook-menu-command");
            stylesheetProperty(entry.content, "color",
                foreground_colours[FOREGROUND_EM]);
            stylesheetProperty(entry.content, "font-weight", "bold");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-logbook-menu-command A");
            stylesheetProperty(entry.content, "color",
                foreground_colours[FOREGROUND_EM]);
            stylesheetProperty(entry.content, "font-weight", "bold");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-logbook-menu-section");
            stylesheetProperty(entry.content, "font-size", "120%");
            stylesheetProperty(entry.content, "margin-top", "5px");
            stylesheetProperty(entry.content, "margin-left", "5px");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-logbook-menu-section A");
            stylesheetProperty(entry.content, "color",
                foreground_colours[FOREGROUND_EM]);
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-logbook-menu-section A:visited");
            stylesheetProperty(entry.content, "color",
                foreground_colours[FOREGROUND_EM]);
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-logbook-menu-question");
            stylesheetProperty(entry.content, "margin-left", "15px");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-logbook-menu-new-q");
            stylesheetProperty(entry.content, "font-style", "italic");
            stylesheetProperty(entry.content, "margin-left", "30px");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-logbook-menu-new-s");
            stylesheetProperty(entry.content, "font-style", "italic");
            stylesheetProperty(entry.content, "font-size", "120%");
            stylesheetProperty(entry.content, "margin-top", "5px");
            stylesheetProperty(entry.content, "margin-left", "5px");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-logbook-collated");
            stylesheetProperty(entry.content, "padding", "8px");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-logbook-collated-name");
            stylesheetProperty(entry.content, "font-weight", "bold");
            stylesheetProperty(entry.content, "font-size", "250%");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-logbook-collated-section-title");
            stylesheetProperty(entry.content, "font-weight", "bold");
            stylesheetProperty(entry.content, "font-size", "200%");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-logbook-collated-section-intro");
            stylesheetProperty(entry.content, "font-style", "italic");
            stylesheetProperty(entry.content, "font-size", "90%");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content,
                ".bs-logbook-collated-question-title");
            stylesheetProperty(entry.content, "font-weight", "bold");
            stylesheetProperty(entry.content, "font-size", "150%");
            stylesheetProperty(entry.content, "margin-left", "16px");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-logbook-collated-question-text");
            stylesheetProperty(entry.content, "font-style", "italic");
            stylesheetProperty(entry.content, "font-size", "90%");
            stylesheetProperty(entry.content, "margin-left", "16px");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-logbook-collated-entry");
            stylesheetProperty(entry.content, "border", "thin black dotted");
            stylesheetProperty(entry.content, "margin", "32px");
            stylesheetProperty(entry.content, "padding", "8px");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-logbook-collated-entry-author");
            stylesheetProperty(entry.content, "font-weight", "bold");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-logbook-collated-entry-date");
            stylesheetProperty(entry.content, "font-style", "italic");
            stylesheetEndEntry(entry.content);

            //stylesheetEntry( entry.content, ".bs-logbook-collated-entry-text
            // P
            //stylesheetProperty( entry.content, "color: #202040}
            //stylesheetEndEntry( entry.content );

            stylesheetEntry(entry.content, ".bs-logbook-entry");
            //stylesheetProperty( entry.content, "background-color", #9090b0;
            stylesheetProperty(entry.content, "border", "thin white dashed");
            stylesheetProperty(entry.content, "margin", "32px");
            stylesheetProperty(entry.content, "padding", "8px");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-logbook-entry-title");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-logbook-entry-author");
            stylesheetProperty(entry.content, "color",
                foreground_colours[FOREGROUND_EM]);
            stylesheetProperty(entry.content, "font-weight", "bold");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-logbook-entry-date");
            stylesheetProperty(entry.content, "color",
                foreground_colours[FOREGROUND_EM]);
            stylesheetProperty(entry.content, "font-style", "italic");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-logbook-entry-draft");
            stylesheetProperty(entry.content, "font-weight", "bold");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-logbook-entry-checkbox");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-logbook-entry-text");
            stylesheetProperty(entry.content, "border-top",
                "thin white dotted");
            stylesheetProperty(entry.content, "margin-top", "8px");
            stylesheetProperty(entry.content, "padding-top", "8px");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".bs-logbook-entry-editlink");
            stylesheetProperty(entry.content, "text-align", "center");
            stylesheetEndEntry(entry.content);
            
            // News feed title text needs margin associated with <h> tags altered:
            stylesheetEntry(entry.content, ".bs-newsfeed-title");
            stylesheetProperty(entry.content, "margin", "2px 0px 5px 0px");
            stylesheetEndEntry(entry.content);

            // News feed body text needs to be rendered the same size as <h6> item titles:
            stylesheetEntry(entry.content, ".bs-newsfeed-content");
            stylesheetProperty(entry.content, "padding", "2px 0px 5px 0px");
            stylesheetProperty(entry.content, "font-size", "75%");
            stylesheetEndEntry(entry.content);

            //  Tree diagram style sheet entries
            stylesheetEntry(entry.content, ".tree, .tree_without_stalk");
            stylesheetProperty(entry.content, "padding-left", "0px");
            stylesheetProperty(entry.content, "padding-right", "16px");
            stylesheetProperty(entry.content, "padding-top", "0px");
            stylesheetProperty(entry.content, "padding-bottom", "0px");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content,
                ".tree_selection .tree, .tree_selection .tree_without_stalk");
            stylesheetProperty(entry.content, "padding-left", "0px");
            stylesheetProperty(entry.content, "padding-right", "16px");
            stylesheetProperty(entry.content, "padding-top", "0px");
            stylesheetProperty(entry.content, "padding-bottom", "12px");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".tree_whole_trimmed_branch");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".tree_trimmed_branch");
            stylesheetProperty(entry.content, "margin-left", "24px");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".tree_branch");
            stylesheetProperty(entry.content, "padding-bottom", "12px");
            stylesheetProperty(entry.content, "background",
                "url(bs_template_tree_vline_black_22_22.gif) repeat-y 0px");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".tree_selection .tree_end_branch");
            stylesheetProperty(entry.content, "padding-bottom", "12px");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".tree_end_branch");
            stylesheetProperty(entry.content, "padding-bottom", "0px");
            stylesheetEndEntry(entry.content);

            // new scheme for naming tree_node classes
            // s = stalk, u = up ( i.e. to senior sibling or to parent ),
            // d = down (i.e. to junior sibling), branch (to child),
            // n = no, y = yes, c = continuation (i.e. skips siblings or
            // child(ren).)
            //
            // some permutations don't exist - if s = n then u and d must be n
            // if u = n then d must be n
            // 
            // tree_node classes differ only in the background image that
            // provides lines

            // 0=n, 1=y, 2=c
            String letters[] = new String[3];
            letters[0] = "n";
            letters[1] = "y";
            letters[2] = "c";
            StringBuffer class_code = new StringBuffer(10);
            for (i = 0; i < 2; i++)
                for (j = 0; j < 3; j++)
                    for (k = 0; k < 3; k++)
                    {
                        if (i == 0 && (j > 0 || k > 0)) continue;
                        if (j == 0 && k > 0) continue;
                        for (l = 0; l < 3; l++)
                        {
                            class_code.setLength(0);
                            class_code.append("s");
                            class_code.append(letters[i]);
                            class_code.append("u");
                            class_code.append(letters[j]);
                            class_code.append("d");
                            class_code.append(letters[k]);
                            class_code.append("b");
                            class_code.append(letters[l]);
                            stylesheetEntry(entry.content, ".tree_node_"
                                + class_code.toString());
                            stylesheetProperty(entry.content, "padding-left",
                                "0px");
                            stylesheetProperty(entry.content, "background",
                                "url(bs_template_tree_node_lines_"
                                    + class_code.toString()
                                    + "_22_22.gif) no-repeat 0px 0px;");
                            stylesheetEndEntry(entry.content);
                        }
                    }

            stylesheetEntry(entry.content, ".tree_node_expander_icon");
            stylesheetProperty(entry.content, "margin", "0px");
            stylesheetProperty(entry.content, "padding", "0px");
            stylesheetProperty(entry.content, "float", "left");
            stylesheetProperty(entry.content, "border", "none");
            stylesheetProperty(entry.content, "width", "12px");
            stylesheetProperty(entry.content, "height", "20px");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".tree_node_icon");
            stylesheetProperty(entry.content, "margin-left", "2px");
            stylesheetProperty(entry.content, "margin-top", "4px");
            stylesheetProperty(entry.content, "float", "left");
            stylesheetProperty(entry.content, "width", "22px");
            stylesheetProperty(entry.content, "height", "22px");
            stylesheetEndEntry(entry.content);

            // level based tree structure entries...

            stylesheetEntry(entry.content, ".tree_siblings_2_without_stalk");
            stylesheetProperty(entry.content, "margin-left", "6px");
            stylesheetProperty(entry.content, "padding-right", "16px");
            stylesheetProperty(entry.content, "padding-bottom", "12px");
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content, ".tree_node_content_1_without_stalk");
            stylesheetProperty(entry.content, "margin-left", "28px");
            stylesheetProperty(entry.content, "margin-right", "0px");
            stylesheetProperty(entry.content, "margin-top", "0px");
            stylesheetProperty(entry.content, "margin-bottom", "0px");
            // can't remember why height property was put in here but it doesn't
            // work well on text blocks - IE treats it as a minimum height
            // but Netscape really messes up - layout assumes this is the
            // height of the block but excess text spills out the bottom of
            // the box over the content below
            //stylesheetProperty( entry.content, "height", "28px" );
            stylesheetEndEntry(entry.content);

            StringBuffer element_spec = new StringBuffer();

            for (i = 1; i <= 10; i++)
            {
                // these could be given varying background colours...
                stylesheetEntry(entry.content, ".tree_siblings_" + i);
                stylesheetProperty(entry.content, "margin-left", "24px");
                stylesheetProperty(entry.content, "padding-right", "16px");
                stylesheetProperty(entry.content, "padding-bottom", "12px");
                stylesheetEndEntry(entry.content);
            }

            element_spec.setLength(0);
            for (i = 1; i <= 10; i++)
            {
                element_spec.append(".tree_node_content_");
                element_spec.append(i);
                if (i < 10) element_spec.append(",");
                element_spec.append("\r\n");
            }
            stylesheetEntry(entry.content, element_spec.toString());
            stylesheetProperty(entry.content, "margin-left", "46px");
            stylesheetProperty(entry.content, "margin-right", "0px");
            stylesheetProperty(entry.content, "margin-top", "0px");
            stylesheetProperty(entry.content, "margin-bottom", "0px");
            //stylesheetProperty( entry.content, "height", "28px" );
            stylesheetEndEntry(entry.content);

            element_spec.setLength(0);
            for (i = 1; i <= 10; i++)
            {
                element_spec.append(".tree_selection .tree_node_content_");
                element_spec.append(i);
                if (i < 10) element_spec.append(",");
                element_spec.append("\r\n");
            }
            stylesheetEntry(entry.content, element_spec.toString());
            stylesheetProperty(entry.content, "margin-left", "46px");
            stylesheetProperty(entry.content, "margin-right", "0px");
            stylesheetProperty(entry.content, "margin-top", "0px");
            stylesheetProperty(entry.content, "margin-bottom", "0px");
            stylesheetProperty(entry.content, "padding-top", "0px");
            stylesheetProperty(entry.content, "padding-bottom", "16px");
            stylesheetProperty(entry.content, "padding-right", "0px");
            stylesheetProperty(entry.content, "padding-left", "0px");
            stylesheetEndEntry(entry.content);

            element_spec.setLength(0);
            for (i = 1; i <= 11; i++)
            {
                if (i == 11)
                    element_spec.append(".tree_node_content_1_without_stalk");
                else
                {
                    element_spec.append(".tree_node_content_");
                    element_spec.append(i);
                }
                element_spec.append(" p");
                if (i < 11) element_spec.append(",");
                element_spec.append("\r\n");
            }
            stylesheetEntry(entry.content, element_spec.toString());
            stylesheetProperty(entry.content, "margin-top", "6px");
            stylesheetProperty(entry.content, "margin-bottom", "4px");
            stylesheetProperty(entry.content, "margin-left", "0px");
            stylesheetProperty(entry.content, "margin-right", "0px");
            stylesheetProperty(entry.content, "padding", "0px");
            stylesheetProperty(entry.content, "font-size", "100%");
            stylesheetProperty(entry.content, "font-weight", "normal");
            stylesheetEndEntry(entry.content);

            element_spec.setLength(0);
            for (i = 1; i <= 11; i++)
            {
                for (j = 1; j <= 6; j++)
                {
                    if (i == 11)
                        element_spec
                            .append(".tree_node_content_1_without_stalk");
                    else
                    {
                        element_spec.append(".tree_node_content_");
                        element_spec.append(i);
                    }
                    element_spec.append(" h");
                    element_spec.append(j);
                    if (i < 11 || j < 6) element_spec.append(",");
                    element_spec.append("\r\n");
                }
            }
            stylesheetEntry(entry.content, element_spec.toString());
            stylesheetProperty(entry.content, "margin-top", "0px");
            stylesheetProperty(entry.content, "margin-bottom", "8px");
            stylesheetProperty(entry.content, "margin-left", "0px");
            stylesheetProperty(entry.content, "margin-right", "0px");
            stylesheetProperty(entry.content, "padding", "0px");
            stylesheetProperty(entry.content, "font-size", "100%");
            stylesheetProperty(entry.content, "font-weight", "bold");
            stylesheetEndEntry(entry.content);

            // outputs new style CSS classes. Prefix indicates this is
            // specifically for resource menus and nothing else. Note
            // that the old style tree classes above are still there to
            // support HTML tagging that is already used in tools (perhaps
            // some 3rd party tools I don't know about) for backwards
            // compatibility. In the future more calls may be made to this
            // method to support other tree diagrams with distinct display
            // options.

            stylesheetEntry(entry.content, ".res_tree_header");
            stylesheetProperty(entry.content, "margin", "0px");
            stylesheetProperty(entry.content, "padding", "6px");
            if (nav_background_colours[BACKGROUND_MENU_HEAD] != null)
                stylesheetProperty(entry.content, "background-color",
                    nav_background_colours[BACKGROUND_MENU_HEAD]);
            stylesheetEndEntry(entry.content);
            stylesheetEntry(entry.content, ".res_tree_big_header");
            stylesheetProperty(entry.content, "margin", "0px");
            stylesheetProperty(entry.content, "padding", "6px");
            if (nav_background_colours[BACKGROUND_MENU_HEAD] != null)
                stylesheetProperty(entry.content, "background-color",
                    nav_background_colours[BACKGROUND_MENU_HEAD]);
            stylesheetEndEntry(entry.content);

            // If either menu highlight colour differs from page backrgound
            // then colours are set in the tree classes. Otherwise the menus
            // are made transparent.
            if (nav_background_colours[BACKGROUND_MENU_1] != null
                && nav_background_colours[BACKGROUND_MENU_2] != null
                && ((!nav_background_colours[BACKGROUND]
                    .equals(nav_background_colours[BACKGROUND_MENU_1])) || (!nav_background_colours[BACKGROUND]
                    .equals(nav_background_colours[BACKGROUND_MENU_2]))))
            {
                compileTreeStylesheet(
                    entry.content,
                    "res_tree",
                    tree_line_path,
                    nav_background_colours[BACKGROUND_MENU_1],
                    nav_foreground_colours,
                    tree_lines ? ("?code=" + tree_line_colour_mapper.toString())
                                    : null,
                    nav_background_colours[BACKGROUND_MENU_2],
                    nav_foreground_colours,
                    tree_lines ? ("?code=" + tree_line_colour_mapper.toString())
                                    : null, 22, res_menu_border_colour);
                compileTreeStylesheet(
                    entry.content,
                    "res_tree_big",
                    tree_lines ? tree_line_path : null,
                    nav_background_colours[BACKGROUND_MENU_1],
                    nav_foreground_colours,
                    tree_lines ? ("?code=" + tree_line_colour_mapper.toString())
                                    : null,
                    nav_background_colours[BACKGROUND_MENU_2],
                    nav_foreground_colours,
                    tree_lines ? ("?code=" + tree_line_colour_mapper.toString())
                                    : null, 44, res_menu_border_colour);
            }
            else
            {
                compileTreeStylesheet(
                    entry.content,
                    "res_tree",
                    tree_lines ? tree_line_path : null,
                    null,
                    null,
                    tree_lines ? ("?code=" + tree_line_colour_mapper.toString())
                                    : null,
                    null,
                    null,
                    tree_lines ? ("?code=" + tree_line_colour_mapper.toString())
                                    : null, 22, res_menu_border_colour);
                compileTreeStylesheet(
                    entry.content,
                    "res_tree_big",
                    tree_lines ? tree_line_path : null,
                    null,
                    null,
                    tree_lines ? ("?code=" + tree_line_colour_mapper.toString())
                                    : null,
                    null,
                    null,
                    tree_lines ? ("?code=" + tree_line_colour_mapper.toString())
                                    : null, 44, res_menu_border_colour);
            }

            entry.last_modified = System.currentTimeMillis();
            author_style_sheets.put(resource_id, entry);
            return entry;
        }
    }

    public String compileAutoStyleSheet(Request req,
        org.bodington.servlet.HttpSession http_session) throws IOException,
        BuildingServerException
    {
        StringBuffer buffer = new StringBuffer(512);

        buffer.append("/*  This is a dynamically generated file  */\n");

        StyleSheetSessionData sss_data = null;

        if (http_session != null)
        {
            sss_data = (StyleSheetSessionData) http_session
                .getAttribute("org.bodington.user_style_sheet_session_data");

            if (sss_data != null && !sss_data.enable_style_sheets)
                return buffer.toString();

            // are we making a transition between unauthenticated into
            // authenticated?
            // in which case there may be options to take into account now.
            if (sss_data != null && sss_data.was_unauth_style
                && req.isAuthenticated() && !req.isAnonymous())
            {
                sss_data.was_unauth_style = false;
                sss_data.number_of_changes++;
                sss_data.last_changed_options = System.currentTimeMillis();

                // if user chose preferences while logging in save them
                if (sss_data.unsaved_changes)
                {
                    // get current values which will be from http_session if
                    // recently changed
                    // and from database if not changed
                    String size = sss_data.getPreference(http_session,
                        "preference.stylesheet.size");
                    String colour_option = sss_data.getPreference(http_session,
                        "preference.stylesheet.colour_option");

                    // put them back and they should be saved to DB this time
                    if (size != null)
                        sss_data.setPreference(http_session,
                            "preference.stylesheet.size", size);
                    if (colour_option != null)
                        sss_data.setPreference(http_session,
                            "preference.stylesheet.colour_option",
                            colour_option);
                }
            }

        }

        BuildingSession session = BuildingSessionManagerImpl.getSession(req
            .getResource());
        String[] style_res_path = session.getStyleResourcePath();
        buffer.append("@import url(");
        buffer.append(req.getContextPath());
        buffer.append(req.getServletPath());
        buffer.append("/");
        for (int i = 0; style_res_path != null && i < style_res_path.length; i++)
        {
            buffer.append(style_res_path[i]);
            buffer.append("/");
        }
        buffer.append("bs_virtual_author.css);\n");

        buffer.append("@import url(");
        buffer.append(req.getContextPath());
        buffer.append(req.getServletPath());
        buffer.append("/");
        for (int i = 0; style_res_path != null && i < style_res_path.length; i++)
        {
            buffer.append(style_res_path[i]);
            buffer.append("/");
        }
        buffer.append("bs_virtual_user");
        if (sss_data != null) buffer.append("_" + sss_data.number_of_changes);
        buffer.append(".css);\n");

        return buffer.toString();
    }

    /**
     * 
     * @param buffer This receives the CSS definitions.
     * @param prefix The prefix used on all class names.
     * @param lines_path Used as path to icons (GIFs).
     * @param background_1 First background colour.
     * @param foreground_1 First foreground colour.
     * @param lines_colour_mapper_code_1 Used to adapt first colours.
     * @param background_2 Second background colour.
     * @param foreground_2 Second foreground colour.
     * @param lines_colour_mapper_code_2 Used to adapt the second colours.
     * @param icon_size Size of the icons (22, 48).
     * @param border_colour Colout of the tree borders.
     * @return The buffer containing the CSS.
     */
    private StringBuffer compileTreeStylesheet(StringBuffer buffer, 
        String prefix,
        String lines_path,
        Color background_1,
        Color[] foreground_1, String lines_colour_mapper_code_1,
        Color background_2,
        Color[] foreground_2, String lines_colour_mapper_code_2, 
        int icon_size,
        Color border_colour)
    {
        int i, j, k, l, hl, d;
        boolean big_icons = icon_size > 22;
        boolean tree_lines = (lines_colour_mapper_code_1 != null && lines_colour_mapper_code_2 != null);

        StringBuffer element_spec = new StringBuffer();
        StringBuffer property_spec = new StringBuffer();

        if (foreground_1 == null) foreground_1 = new Color[7];
        if (foreground_2 == null) foreground_2 = new Color[7];

        Color strHL, strTC;

        boolean highlight_branches = true;
        boolean highlight_nodes = true;
        boolean highlight_content = false;

        // get rid of border if no backgrounds
        if (border_colour != null && background_1 == null
            && background_2 == null) border_colour = null;

        // get rid of border if the colour is the same as the background
        if (border_colour != null && background_1 != null
            && border_colour.equals(background_1)) border_colour = null;

        boolean border_branches = border_colour != null;
        boolean border_nodes = border_colour != null;
        int border_width = (border_colour != null) ? 1 : 0;
        String res_menu_border_style = (border_width > 0) ? ("solid "
            + border_width + "px #" + Integer.toHexString(
            border_colour.getRGB()).substring(2)) : null;

        stylesheetEntry(buffer, ".clearer");
        stylesheetProperty(buffer, "clear", "left");
        stylesheetProperty(buffer, "line-height", "0");
        stylesheetProperty(buffer, "height", "0");
        stylesheetEndEntry(buffer);

        stylesheetEntry(buffer, "." + prefix);
        stylesheetProperty(buffer, "margin", "0.5em");
        //stylesheetProperty( buffer, "padding", "0px" );
        if (border_width > 0 && res_menu_border_style != null
            && res_menu_border_style.length() > 0)
            stylesheetProperty(buffer, "border-right", res_menu_border_style);
        stylesheetEndEntry(buffer);

        // silly extra div needed for M$ ie6 because of bug in CSS handling.
        // In ie6 the background graphic on a div is misplaced if there is a div
        // (or p) inside the div with the background. The background image
        // origin
        // coordinate is shifted to the OUTSIDE of the border instead of the
        // INSIDE
        // of the border which is correct. Ie6 cannot be forced into correct
        // behaviour
        // by use of "standards compatibility mode" but can be fooled if a width
        // OR
        // height property is applied to the div with the background image.
        // However,
        // that doesn't work here because 100% width is also treated differently
        // by
        // ie6 and others. So only solution is to never put background images
        // and
        // border on the same div if there may be other divs inside that need to
        // line up!
        stylesheetEntry(buffer, "." + prefix + "_branch_border");
        if (border_branches)
        {
            stylesheetProperty(buffer, "background-color", border_colour);
            stylesheetProperty(buffer, "padding", "" + border_width
                + "px 0px 0px " + border_width + "px");
        }
        else
            stylesheetProperty(buffer, "padding", "0px");
        stylesheetProperty(buffer, "margin", "0px");
        //stylesheetProperty( buffer, "border-top", res_menu_border_style );
        //stylesheetProperty( buffer, "border-left", res_menu_border_style );
        stylesheetEndEntry(buffer);
        stylesheetEntry(buffer, "." + prefix + "_end_branch_border");
        if (border_branches)
        {
            stylesheetProperty(buffer, "background-color", border_colour);
            stylesheetProperty(buffer, "padding", "" + border_width + "px 0px "
                + border_width + "px " + border_width + "px");
        }
        else
            stylesheetProperty(buffer, "padding", "0px");
        stylesheetProperty(buffer, "margin", "0px");
        //stylesheetProperty( buffer, "border-top", res_menu_border_style );
        //stylesheetProperty( buffer, "border-left", res_menu_border_style );
        //stylesheetProperty( buffer, "border-bottom", res_menu_border_style );
        stylesheetEndEntry(buffer);

        // one of these surrounds a node that has children
        // first provides a vertical line to next node and second
        // lacks that line. A branch has a node and a sibling set
        // for the children.
        for (hl = 1; hl <= 2; hl++)
        {
            // level 1 branch must be highlighted if nodes/content are
            stylesheetEntry(buffer, "." + prefix + "_branch_lev1_hl" + hl);
            stylesheetProperty(buffer, "margin", "0px");
            stylesheetProperty(buffer, "padding", "0px 0px 0px 0px");
            if (hl == 1)
                strHL = background_1;
            else
                strHL = background_2;
            if (hl == 1)
                strTC = foreground_1[FOREGROUND];
            else
                strTC = foreground_2[FOREGROUND];
            if ((highlight_branches /*
                                                  * || highlight_nodes ||
                                                  * highlight_content
                                                  */) && strHL != null)
                stylesheetProperty(buffer, "background-color", strHL);

            stylesheetEndEntry(buffer);
            // other levels
            element_spec.setLength(0);
            for (i = 2; i <= 10; i++)
            {
                element_spec.append("." + prefix + "_branch_lev");
                element_spec.append(i);
                element_spec.append("_hl");
                element_spec.append(hl);
                if (i < 10) element_spec.append(",");
                element_spec.append("\r\n");
            }
            stylesheetEntry(buffer, element_spec.toString());
            stylesheetProperty(buffer, "margin", "0px");
            stylesheetProperty(buffer, "padding", "0px 0px 0px 0px");
            if (hl == 1)
                strHL = background_1;
            else
                strHL = background_2;
            if (hl == 1)
                strTC = foreground_1[FOREGROUND];
            else
                strTC = foreground_2[FOREGROUND];
            if (tree_lines)
            {
                property_spec.setLength(0);
                property_spec.append("url(");
                property_spec.append(lines_path);
                property_spec.append("tree_vline_black_" + icon_size + "_"
                    + icon_size + ".gif");
                property_spec.append((hl == 1) ? lines_colour_mapper_code_1
                                : lines_colour_mapper_code_2);
                property_spec.append(") ");
                stylesheetProperty(buffer, "background-image", property_spec
                    .toString());
                //stylesheetProperty( buffer, "width", "100%" );
                stylesheetProperty(buffer, "background-repeat", "repeat-y");
                stylesheetProperty(buffer, "background-position", "top left");
            }
            if (highlight_branches && strHL != null)
                stylesheetProperty(buffer, "background-color", strHL);

            //if ( border_branches && res_menu_border_style!=null &&
            // res_menu_border_style.length()>0 )
            //{
            //    stylesheetProperty( buffer, "border-top", res_menu_border_style
            // );
            //    stylesheetProperty( buffer, "border-left", res_menu_border_style
            // );
            //    //stylesheetProperty( buffer, "border", strTC );
            //}
            stylesheetEndEntry(buffer);
        }

        for (hl = 1; hl <= 2; hl++)
        {
            // level 1 branch must be highlighted if nodes/content are
            stylesheetEntry(buffer, "." + prefix + "_end_branch_lev1_hl" + hl);
            stylesheetProperty(buffer, "margin", "0px");
            stylesheetProperty(buffer, "padding", "0px 0px 0px 0px");
            if (hl == 1)
                strHL = background_1;
            else
                strHL = background_2;
            if (hl == 1)
                strTC = foreground_1[FOREGROUND];
            else
                strTC = foreground_2[FOREGROUND];

            if ((highlight_branches /*
                                                  * || highlight_nodes ||
                                                  * highlight_content
                                                  */) && strHL != null)
                stylesheetProperty(buffer, "background", strHL);

            //if ( border_branches && res_menu_border_style!=null &&
            // res_menu_border_style.length()>0 )
            //{
            //    stylesheetProperty( buffer, "border-top", res_menu_border_style
            // );
            //    stylesheetProperty( buffer, "border-bottom",
            // res_menu_border_style );
            //    stylesheetProperty( buffer, "border-left", res_menu_border_style
            // );
            //    //stylesheetProperty( buffer, "border", strTC );
            //}
            stylesheetEndEntry(buffer);
            // other levels
            element_spec.setLength(0);
            for (i = 2; i <= 10; i++)
            {
                element_spec.append("." + prefix + "_end_branch_lev");
                element_spec.append(i);
                element_spec.append("_hl");
                element_spec.append(hl);
                if (i < 10) element_spec.append(",");
                element_spec.append("\r\n");
            }
            stylesheetEntry(buffer, element_spec.toString());
            stylesheetProperty(buffer, "margin", "0px");
            stylesheetProperty(buffer, "padding", "0px 0px 0px 0px");
            if (hl == 1)
                strHL = background_1;
            else
                strHL = background_2;
            if (highlight_branches && strHL != null)
                stylesheetProperty(buffer, "background", strHL);
            //if ( border_branches && res_menu_border_style!=null &&
            // res_menu_border_style.length()>0 )
            //{
            //    stylesheetProperty( buffer, "border-top", res_menu_border_style
            // );
            //    stylesheetProperty( buffer, "border-bottom",
            // res_menu_border_style );
            //    stylesheetProperty( buffer, "border-left", res_menu_border_style
            // );
            //    //stylesheetProperty( buffer, "border", strTC );
            //}
            stylesheetEndEntry(buffer);
        }

        // new scheme for naming tree_node classes
        // s = stalk, u = up ( i.e. to senior sibling or to parent ),
        // d = down (i.e. to junior sibling), branch (to child),
        // n = no, y = yes, c = continuation (i.e. skips siblings or
        // child(ren).)
        //
        // some permutations don't exist - if s = n then u and d must be n
        // if u = n then d must be n
        // 
        // level and highlight variants are used so that background images
        // can have colour alterations for visibility against highlight colours.
        // tree_node classes differ only in the background image that provides
        // lines

        // 0=n, 1=y, 2=c
        String letters[] = new String[3];
        letters[0] = "n";
        letters[1] = "y";
        letters[2] = "c";
        StringBuffer class_code = new StringBuffer(10);
        for (i = 0; i < 2; i++)
            for (j = 0; j < 3; j++)
                for (k = 0; k < 3; k++)
                {
                    if (i == 0 && (j > 0 || k > 0)) continue;
                    if (j == 0 && k > 0) continue;
                    for (l = 0; l < 3; l++)
                    {
                        class_code.setLength(0);
                        class_code.append("s");
                        class_code.append(letters[i]);
                        class_code.append("u");
                        class_code.append(letters[j]);
                        class_code.append("d");
                        class_code.append(letters[k]);
                        class_code.append("b");
                        class_code.append(letters[l]);
                        for (hl = 1; hl <= 2; hl++)
                        {
                            element_spec.setLength(0);
                            for (d = 1; d <= 10; d++)
                            {
                                element_spec.append("." + prefix + "_node_");
                                element_spec.append(class_code.toString());
                                element_spec.append("_lev");
                                element_spec.append(d);
                                element_spec.append("_hl");
                                element_spec.append(hl);
                                if (d < 10) element_spec.append(", ");
                                element_spec.append("\r\n");
                            }
                            stylesheetEntry(buffer, element_spec.toString());
                            stylesheetProperty(buffer, "margin", "0px");
                            stylesheetProperty(buffer, "padding",
                                "0em 0em 0em 0em");
                            if (hl == 1)
                                strHL = background_1;
                            else
                                strHL = background_2;
                            property_spec.setLength(0);
                            if (tree_lines)
                            {
                                property_spec.append("url(");
                                property_spec.append(lines_path);
                                property_spec.append("tree_node_lines_");
                                property_spec.append(class_code.toString());
                                property_spec.append("_" + icon_size + "_"
                                    + icon_size + ".gif");
                                property_spec
                                    .append((hl == 1) ? lines_colour_mapper_code_1
                                                    : lines_colour_mapper_code_2);
                                property_spec.append(") ");
                                stylesheetProperty(buffer, "background-image",
                                    property_spec.toString());
                                stylesheetProperty(buffer, "background-repeat",
                                    "no-repeat");
                                stylesheetProperty(buffer, "background-position",
                                    "top left");
                            }
   
                            if (highlight_nodes && strHL != null)
                                stylesheetProperty(buffer, "background-color",
                                    strHL);
                            //if ( border_nodes && res_menu_border_style!=null
                            // && res_menu_border_style.length()>0 )
                            //{
                            //stylesheetProperty( buffer, "border-right",
                            // res_menu_border_style );
                            //stylesheetProperty( buffer, "border-bottom",
                            // res_menu_border_style );
                            //stylesheetProperty( buffer, "border-left",
                            // res_menu_border_style );
                            //stylesheetProperty( buffer, "border", strTC );
                            //}
                            stylesheetEndEntry(buffer);
                        }
                    }
                }
        buffer.append(" /* Hides from IE5-mac \\*/\n");
        for (i = 0; i < 2; i++)
            for (j = 0; j < 3; j++)
                for (k = 0; k < 3; k++)
                {
                    if (i == 0 && (j > 0 || k > 0)) continue;
                    if (j == 0 && k > 0) continue;
                    for (l = 0; l < 3; l++)
                    {
                        class_code.setLength(0);
                        class_code.append("s");
                        class_code.append(letters[i]);
                        class_code.append("u");
                        class_code.append(letters[j]);
                        class_code.append("d");
                        class_code.append(letters[k]);
                        class_code.append("b");
                        class_code.append(letters[l]);
                        for (hl = 1; hl <= 2; hl++)
                        {

                            for (d = 1; d <= 10; d++)
                            {
                                element_spec.setLength(0);
                                element_spec.append("." + prefix + "_node_");
                                element_spec.append(class_code.toString());
                                element_spec.append("_lev");
                                element_spec.append(d);
                                element_spec.append("_hl");
                                element_spec.append(hl);

                                /*
                                 * Hack for the IE Peaaboo Bug
                                 * http://www.positioniseverything.net/articles/hollyhack.html#haslayout
                                 */

                                buffer.append("* html  "
                                    + element_spec.toString() + ",");
                                buffer.append("\n");

                            }
                        }
                    }
                }
        buffer.append("* html .doesnotexist\n");
        buffer.append(" { height: 1%; }\n");
        buffer.append("/* End hide from IE5-mac */\n");

        // The expander/collapser icon can't go in the background of the
        // node because it needs to be clicked on. Style makes sure it
        // lines up with the background image.
        stylesheetEntry(buffer, "." + prefix + "_node_expander_icon");
        stylesheetProperty(buffer, "margin", "0px 0px 0px 3px");
        stylesheetProperty(buffer, "padding", "0px");
        stylesheetProperty(buffer, "float", "left");
        stylesheetProperty(buffer, "border", "none");
        //stylesheetProperty( buffer, "width", "12px" );
        //stylesheetProperty( buffer, "height", "20px" );
        stylesheetEndEntry(buffer);

        // To line things up without or without stalk or expander
        // icon there are three alternate styles for the 22 by 22 icon
        // that represents the node.
        stylesheetEntry(buffer, "." + prefix + "_node_icon");
        stylesheetProperty(buffer, "margin", "4px 6px 0px "
            + (6 * (icon_size / 22)) + "px");
        stylesheetProperty(buffer, "padding", "0px");
        stylesheetProperty(buffer, "float", "left");
        stylesheetProperty(buffer, "border", "none");
        //stylesheetProperty( buffer, "width", "22px" );
        //stylesheetProperty( buffer, "height", "22px" );
        stylesheetEndEntry(buffer);

        stylesheetEntry(buffer, "." + prefix + "_node_icon_without_stalk");
        stylesheetProperty(buffer, "margin", "4px 6px 0px "
            + (3 * (icon_size / 22)) + "px");
        stylesheetProperty(buffer, "padding", "0px");
        stylesheetProperty(buffer, "float", "left");
        stylesheetProperty(buffer, "border", "none");
        //stylesheetProperty( buffer, "width", "22px" );
        //stylesheetProperty( buffer, "height", "22px" );
        stylesheetEndEntry(buffer);

        stylesheetEntry(buffer, "." + prefix + "_node_icon_no_expander");
        stylesheetProperty(buffer, "margin", "4px 6px 0px "
            + ((tree_lines ? 21 : 6) * (icon_size / 22)) + "px");
        stylesheetProperty(buffer, "padding", "0px");
        stylesheetProperty(buffer, "float", "left");
        stylesheetProperty(buffer, "border", "none");
        //stylesheetProperty( buffer, "width", "22px" );
        //stylesheetProperty( buffer, "height", "22px" );
        stylesheetEndEntry(buffer);

        // level based tree structure entries...
        // (So different style could be applied to nodes at
        // different depths in the tree. Actually a cycle of
        // ten levels.)

        // siblings refers to a set of sibling nodes that are in the
        // second half of a branch, after the parent node.
        for (i = 1; i <= 10; i++)
        {
            // these could be given varying background colours...
            stylesheetEntry(buffer, "." + prefix + "_siblings_lev" + i);
            stylesheetProperty(buffer, "margin", "0px 0px 0px "
                + ((tree_lines ? 24 : 6) * (icon_size / 22) - border_width)
                + "px");
            stylesheetProperty(buffer, "padding", "0px 0px 0px 0px");
            //stylesheetProperty( buffer, "background", randomColour() );
            stylesheetEndEntry(buffer);
        }

        // If level 1 lacks a stalk, level 2 siblings need less
        // indenting.
        stylesheetEntry(buffer, "." + prefix + "_siblings_without_stalk_lev2");
        stylesheetProperty(buffer, "margin", "0px 0px 0px "
            + (6 * (icon_size / 22) - border_width) + "px");
        stylesheetProperty(buffer, "padding", "0px 0px 0px 0px");
        //stylesheetProperty( buffer, "background", randomColour() );
        stylesheetEndEntry(buffer);

        // always transparent empty used after last node in siblings to
        // provide space that can have a border on right side
        stylesheetEntry(buffer, "." + prefix + "_siblings_padding");
        stylesheetProperty(buffer, "margin", "0px 0px 0px 0px");
        stylesheetProperty(buffer, "padding", "0px 0px 12px 0px");
        //if ( border_nodes && res_menu_border_style!=null &&
        // res_menu_border_style.length()>0 )
        //stylesheetProperty( buffer, "border-right", res_menu_border_style );
        stylesheetEndEntry(buffer);

        // tree node content is the part of the node after the
        // expander icon and the node icon which actually describes
        // the node. There can be more than one content block in a
        // node.
        // Here all levels are defined the same.
        for (hl = 1; hl <= 2; hl++)
        {
            element_spec.setLength(0);
            for (i = 1; i <= 10; i++)
            {
                element_spec.append("." + prefix + "_node_content_lev");
                element_spec.append(i);
                element_spec.append("_hl");
                element_spec.append(hl);
                if (i < 10) element_spec.append(",");
                element_spec.append("\r\n");
            }
            stylesheetEntry(buffer, element_spec.toString());
            stylesheetProperty(buffer, "display", "block");
            stylesheetProperty(buffer, "margin", "0px 0px 0px "
                + ((tree_lines ? 50 : 36) * (icon_size / 22)) + "px");
            stylesheetProperty(buffer, "padding", "4px 0px 4px 0px");
            if (hl == 1)
                strHL = background_1;
            else
                strHL = background_2;
            if (hl == 1)
                strTC = foreground_1[FOREGROUND];
            else
                strTC = foreground_2[FOREGROUND];
            if (highlight_content && strHL != null)
                stylesheetProperty(buffer, "background", strHL);
            //                if ( (highlight_content || highlight_nodes || highlight_branches)
            // && strTC!=null )
            //                    stylesheetProperty( buffer, "color", strTC ); // seems to prevent
            // link colour settings to be used.
            stylesheetEndEntry(buffer);
        }

        // less indenting at root node if root node lacks stalk.
        for (hl = 1; hl <= 2; hl++)
        {
            element_spec.setLength(0);
            element_spec.append("." + prefix
                + "_node_content_without_stalk_lev1_hl");
            element_spec.append(hl);
            stylesheetEntry(buffer, element_spec.toString());
            stylesheetProperty(buffer, "display", "block");
            stylesheetProperty(buffer, "margin", "0px 0px 0px "
                + (36 * (icon_size / 22)) + "px");
            stylesheetProperty(buffer, "padding", "4px 0px 4px 0px");
            if (hl == 1)
                strHL = background_1;
            else
                strHL = background_2;
            if (hl == 1)
                strTC = foreground_1[FOREGROUND];
            else
                strTC = foreground_2[FOREGROUND];
            if (highlight_content && strHL != null)
                stylesheetProperty(buffer, "background", strHL);
            if ((highlight_content) && strTC != null)
                stylesheetProperty(buffer, "color", strTC);
            stylesheetEndEntry(buffer);
        }

        // The following are for styling actual text within the node.

        // This is intended for styling headings that introduce the
        // the node. Use is optional. If required, this should enclose
        // the icons and then the text should be enclosed by span with
        // class="tree_node_content_x".
        element_spec.setLength(0);
        for (i = 1; i <= 10; i++)
        {
            element_spec.append("." + prefix + "_node_title_lev");
            element_spec.append(i);
            if (i < 10) element_spec.append(",");
            element_spec.append("\r\n");
        }
        stylesheetEntry(buffer, element_spec.toString());
        stylesheetProperty(buffer, "margin", "0px 0px 0px 0px");
        stylesheetProperty(buffer, "padding", "0px 0px 0px 0px");
        stylesheetProperty(buffer, "vertical-align", "bottom");
        stylesheetProperty(buffer, "font-size", "100%");
        stylesheetProperty(buffer, "font-weight", "bold");
        //stylesheetProperty( buffer, "height", "28px" );
        stylesheetEndEntry(buffer);

        // headings go in their
        stylesheetEntry(buffer, "." + prefix + "_heading_node_title");
        stylesheetProperty(buffer, "margin", "0px 0px 0px "
            + ((tree_lines ? 50 : 35) * (icon_size / 22)) + "px");
        stylesheetProperty(buffer, "padding", "5px 0px 0px 0px");
        stylesheetProperty(buffer, "text-align", "left");//uhi:awc
        // changed
        // text-align
        // to
        // left
        stylesheetProperty(buffer, "font-size", "100%");
        stylesheetProperty(buffer, "font-weight", "bold");
        stylesheetEndEntry(buffer);
        
        //if ( border_nodes && res_menu_border_style!=null &&
        // res_menu_border_style.length()>0 )
        //    stylesheetProperty( buffer, "border-right", res_menu_border_style );
//        stylesheetEndEntry(buffer);

        // Styles content in links and links in content to have proper
        // colours depending on highlight style
        for (d = 0; d < 4; d++)
            for (hl = 1; hl <= 2; hl++)
            {
                element_spec.setLength(0);
                strTC = null;
                for (i = 1; i <= 11; i++)
                {
                    if (i == 11)
                        element_spec.append("." + prefix
                            + "_node_content_without_stalk_lev1_hl");
                    else
                    {
                        element_spec.append("." + prefix + "_node_content_lev");
                        element_spec.append(i);
                        element_spec.append("_hl");
                    }
                    element_spec.append(hl);
                    switch (d)
                    {
                        case 0:
                            element_spec.append(" a:link, a:link ");
                            if (hl == 1)
                                strTC = foreground_1[FOREGROUND_LINK];
                            else
                                strTC = foreground_2[FOREGROUND_LINK];
                            break;
                        case 1:
                            element_spec.append(" a:visited, a:visited ");
                            if (hl == 1)
                                strTC = foreground_1[FOREGROUND_VISITED];
                            else
                                strTC = foreground_2[FOREGROUND_VISITED];
                            break;
                        case 2:
                            element_spec.append(" a:active, a:active ");
                            if (hl == 1)
                                strTC = foreground_1[FOREGROUND_ACTIVE];
                            else
                                strTC = foreground_2[FOREGROUND_ACTIVE];
                            break;
                        case 3:
                            element_spec.append(" a:hover, a:hover ");
                            if (hl == 1)
                                strTC = foreground_1[FOREGROUND_HOVER];
                            else
                                strTC = foreground_2[FOREGROUND_HOVER];
                            break;
                    }
                    if (i == 11)
                        element_spec.append("." + prefix
                            + "_node_content_without_stalk_lev1_hl");
                    else
                    {
                        element_spec.append("." + prefix + "_node_content_lev");
                        element_spec.append(i);
                        element_spec.append("_hl");
                    }
                    element_spec.append(hl);
                    if (i < 11) element_spec.append(",");
                    element_spec.append("\r\n");
                }
                stylesheetEntry(buffer, element_spec.toString());
                if ((highlight_content) && strTC != null)
                    stylesheetProperty(buffer, "color", strTC);
                stylesheetEndEntry(buffer);
            }

        // Styles p elements inside all types of content div.
        // puts all paragraph spacing to avoid margin spilling out
        // of node.
        element_spec.setLength(0);
        for (i = 1; i <= 11; i++)
        {
            if (i == 11)
                element_spec.append("." + prefix
                    + "_node_content_without_stalk_lev1_hl1 p, ");
            else
            {
                element_spec.append("." + prefix + "_node_content_lev");
                element_spec.append(i);
                element_spec.append("_hl1 p, ");
            }
            if (i == 11)
                element_spec.append("." + prefix
                    + "_node_content_without_stalk_lev1_hl2 p ");
            else
            {
                element_spec.append("." + prefix + "_node_content_lev");
                element_spec.append(i);
                element_spec.append("_hl2 p");
            }
            if (i < 11) element_spec.append(",");
            element_spec.append("\r\n");
        }
        stylesheetEntry(buffer, element_spec.toString());
        stylesheetProperty(buffer, "margin", "0em 0em 0em 0em");
        stylesheetProperty(buffer, "padding", "0.25em 0em 0em 0em");
        stylesheetProperty(buffer, "font-size", "100%");
        stylesheetProperty(buffer, "font-weight", "normal");
        //stylesheetProperty( buffer, "border", "1px solid black" );
        stylesheetEndEntry(buffer);

        // Styles all heading levels to be the same. Indenting will
        // show heading level visually but audible browser will go by
        // the heading level tag. Without CSS the headings will revert
        // to varying size.
        element_spec.setLength(0);
        for (i = 1; i <= 11; i++)
        {
            for (j = 1; j <= 6; j++)
            {
                if (i == 11)
                    element_spec.append("." + prefix
                        + "_node_content_without_stalk_lev1_hl1 h");
                else
                {
                    element_spec.append("." + prefix + "_node_content_lev");
                    element_spec.append(i);
                    element_spec.append("_hl1 h");
                }
                element_spec.append(j);
                element_spec.append(", ");
                if (i == 11)
                    element_spec.append("." + prefix
                        + "_node_content_without_stalk_lev1_hl2 h");
                else
                {
                    element_spec.append("." + prefix + "_node_content_lev");
                    element_spec.append(i);
                    element_spec.append("_hl2 h");
                }
                element_spec.append(j);

                if (i < 11 || j < 6) element_spec.append(",");
                element_spec.append("\r\n");
            }
        }
        stylesheetEntry(buffer, element_spec.toString());
        stylesheetProperty(buffer, "margin", "0.5em 0em 0em 0em");
        stylesheetProperty(buffer, "padding", "0px");
        // Bug-fix: commenting out the line below fixes RT #913466, #913844.
        //stylesheetProperty(buffer, "font-size", "100%");
        stylesheetProperty(buffer, "font-weight", "bold");
        //stylesheetProperty( buffer, "border", "1px solid black" );
        stylesheetEndEntry(buffer);

        // The following will allow a branch of the tree to be
        // marked as selected and display differently.

        //stylesheetEntry( buffer, "." + prefix + "_selection .tree, ." +
        // prefix + "_selection ." + prefix + "_without_stalk" );
        //stylesheetProperty( buffer, "margin", "0px 0px 12px 0px" );
        //stylesheetEndEntry( buffer );
        //stylesheetEntry( buffer, "." + prefix + "_selection ." + prefix +
        // "_end_branch" );
        //stylesheetProperty( buffer, "padding-bottom", "12px" );
        //stylesheetEndEntry( buffer );

        //element_spec.setLength( 0 );
        //for ( i=1; i<=10; i++ )
        //{
        //    element_spec.append( "." + prefix + "_selection ." + prefix +
        // "_node_content_" );
        //    element_spec.append( i );
        //    if ( i<10 )
        //        element_spec.append( "," );
        //    element_spec.append( "\r\n" );
        //}
        //stylesheetEntry( buffer, element_spec.toString() );
        //stylesheetProperty( buffer, "margin", "0px 0px 16px 46px" );
        //stylesheetProperty( buffer, "padding", "0px 0px 16px 0px" );
        //stylesheetEndEntry( buffer );

        return buffer;
    }

    /**
     * Create a stylesheet for the current user.
     */
    private StyleSheetEntry compileUserStyleSheet(Request req,
        org.bodington.servlet.HttpSession http_session,
        StyleSheetSessionData sss_data, PrimaryKey user_id,
        StyleSheetEntry entry) throws BuildingServerException
    {
        int i, j, k, b, f;
        StringBuffer element_spec = new StringBuffer();

        log.debug("Compiling User Style Sheet");

        BuildingSession building_session = req.getBuildingSession();

        synchronized (http_session)
        {
            String size = null;
            String colour_option = null;

            size = sss_data.getPreference(http_session,
                "preference.stylesheet.size");
            colour_option = sss_data.getPreference(http_session,
                "preference.stylesheet.colour_option");

            if (colour_option == null) colour_option = "normal";
            if (size == null) size = "normal";

            if (entry == null) entry = new StyleSheetEntry();
            entry.content = new StringBuffer();

            entry.content
                .append("/* Bodington Virtual Style Sheet - User's */\r\n\r\n");
            // instead of importing the author style sheet we can reference both
            // style sheets in the HTML with two LINK elements
            //entry.content.append( "@import
            // url('bs_virtual_author.css');\r\n\r\n" );

            if ("normal".equals(size) && "normal".equals(colour_option))
            {
                entry.last_modified = System.currentTimeMillis();
                return entry;
            }

            boolean change_size = false, change_colour = false;
            String font_size = "100%";

            if ("big".equals(size))
            {
                change_size = true;
                font_size = "150%";
            }

            // text size is always relative so needs to be incrased only at top
            // level
            if (change_size)
            {
                for (k = 0; k < 2; k++)
                {
                    stylesheetEntry(entry.content, k == 0 ? "body"
                                    : ".bodington_navigation_page");
                    stylesheetProperty(entry.content, "font-size", font_size);
                    stylesheetEndEntry(entry.content);
                }
            }

            change_colour = !"normal".equals(colour_option);

            if (change_colour)
            {
                // null value indicates that the user style sheet should not
                // change the colour.
                // starting to reform and simplify colour processing
                Color[][] foreground_colours = new Color[2][];
                foreground_colours[0] = getForegroundColors(building_session,
                    "style");
                foreground_colours[1] = getForegroundColors(building_session,
                    "style_navigation");
                Color[][] background_colours = new Color[2][];
                background_colours[0] = getBackgroundColors(building_session,
                    "style");
                background_colours[1] = getBackgroundColors(building_session,
                    "style_navigation");
                Color res_menu_border_colour = building_session.getPropertyColor(
                    "style_navigation_menu_border_colour", null);
                boolean tree_lines = "yes".equalsIgnoreCase(building_session
                    .getProperty("ui_navigation_menu_lines", "yes"));

                // one colour mapper is needed for each background colour
                ColourPreferenceMapper[][] colour_mapper_list = new ColourPreferenceMapper[2][6];
                Color[][] preferred_background_colours = new Color[2][6];
                boolean[][] background_colour_changed = new boolean[2][6];
                Color[][][] preferred_foreground_colours = new Color[2][6][7];
                boolean[][][] foreground_colour_changed = new boolean[2][6][7];

                for (k = 0; k < 2; k++)
                    for (i = 0; i < 6; i++)
                        // start with no-transform mappers
                        colour_mapper_list[k][i] = new ColourPreferenceMapper();

                // transform each of the colour mappers so foreground colours
                // can
                // be mapped for each background colour they may appear against
                for (k = 0; k < 2; k++)
                {
                    for (i = 0; i < colour_mapper_list[k].length; i++)
                    {
                        if (background_colours[k][i] == null)
                        {
                            colour_mapper_list[k][i] = null;
                            continue;
                        }
                        colour_mapper_list[k][i].setReferenceColours(
                            background_colours[k][i],
                            foreground_colours[k][FOREGROUND]);
                        ColourPreferenceMapper.Adjust mapping = ColourPreferenceMapper.Adjust
                            .getMapping(colour_option);
                        if (mapping != null)
                            colour_mapper_list[k][i].setType(mapping);
                    }
                }

                // now map all the colours against all backgrounds
                for (k = 0; k < 2; k++)
                {
                    for (i = 0; i < colour_mapper_list[k].length; i++)
                    {
                        if (colour_mapper_list[k][i] == null)
                        {
                            preferred_background_colours[k][i] = null;
                            background_colour_changed[k][i] = false;
                        }
                        else
                        {
                            preferred_background_colours[k][i] = colour_mapper_list[k][i]
                                .getPreferredBackgroundColor();
                            background_colour_changed[k][i] = !preferred_background_colours[k][i]
                                .equals(background_colours[k][i]);
                        }
                        // all perms of foreground against background
                        for (j = 0; j < foreground_colours[k].length; j++)
                        {
                            if (colour_mapper_list[k][i] == null)
                            {
                                preferred_foreground_colours[k][i][j] = null;
                                foreground_colour_changed[k][i][j] = false;
                            }
                            else
                            {
                                preferred_foreground_colours[k][i][j] = colour_mapper_list[k][i]
                                    .getPreferredForegroundColor(foreground_colours[k][j]);
                                foreground_colour_changed[k][i][j] = !preferred_foreground_colours[k][i][j]
                                    .equals(foreground_colours[k][j]);
                            }
                        }

                    }
                }

                // These lines of code are about determining the colour of lines
                // in the background
                // of tree diagrams. The code determined will be used to
                // transform the colour of
                // the GIF files via a query string to the servlet that
                // processed them.
                // Line will be black or white depending on background colour of
                // page
                int effective_background_1, effective_background_2;
                effective_background_1 = (colour_mapper_list[1][BACKGROUND_MENU_1] != null) ? BACKGROUND_MENU_1
                                : BACKGROUND;
                effective_background_2 = (colour_mapper_list[1][BACKGROUND_MENU_2] != null) ? BACKGROUND_MENU_2
                                : BACKGROUND;

                ColourPreferenceMapper tree_line_colour_mapper_1 = null;
                ColourPreferenceMapper tree_line_colour_mapper_2 = null;
                if (tree_lines)
                {
                    tree_line_colour_mapper_1 = new ColourPreferenceMapper(
                        colour_mapper_list[1][effective_background_1]
                            .toString());
                    tree_line_colour_mapper_2 = new ColourPreferenceMapper(
                        colour_mapper_list[1][effective_background_2]
                            .toString());

                    Color tree_line_colour = null;
                    float[] hsb;
                    // if background is lighter than mid grey use black lines
                    // otherwise use white lines.
                    hsb = Color.RGBtoHSB(
                        preferred_background_colours[1][effective_background_1]
                            .getRed(),
                        preferred_background_colours[1][effective_background_1]
                            .getGreen(),
                        preferred_background_colours[1][effective_background_1]
                            .getBlue(), null);
                    tree_line_colour = new Color((hsb[2] < 0.5) ? 0xffffff
                                    : 0x000000);
                    tree_line_colour_mapper_1
                        .setReferenceColours(
                            preferred_background_colours[1][effective_background_1],
                            tree_line_colour);
                    hsb = Color.RGBtoHSB(
                        preferred_background_colours[1][effective_background_2]
                            .getRed(),
                        preferred_background_colours[1][effective_background_2]
                            .getGreen(),
                        preferred_background_colours[1][effective_background_2]
                            .getBlue(), null);
                    tree_line_colour = new Color((hsb[2] < 0.5) ? 0xffffff
                                    : 0x000000);
                    tree_line_colour_mapper_2
                        .setReferenceColours(
                            preferred_background_colours[1][effective_background_2],
                            tree_line_colour);
                }

                // colours all worked out, now output stylesheet

                // do all doc page stuff and then nav pages
                for (k = 0; k < 2; k++)
                {
                    for (i = 0; i <= BACKGROUND_MENU_HEAD; i++)
                    {
                        for (b = 0; b < css_back_elements[i].length; b++)
                        {
                            // adapt all the background colours
                            if (background_colour_changed[k][i])
                            {
                                element_spec.setLength(0);
                                if (k == 1)
                                    element_spec
                                        .append(".bodington_navigation_page ");
                                // don't need body in navigation pages
                                if (k == 0
                                    || !"body".equals(css_back_elements[i][b]))
                                    element_spec
                                        .append(css_back_elements[i][b]);

                                stylesheetEntry(entry.content, element_spec
                                    .toString());
                                stylesheetProperty(entry.content,
                                    "background-color",
                                    preferred_background_colours[k][i]);
                                stylesheetProperty(entry.content,
                                    "background-image", "none");
                                stylesheetEndEntry(entry.content);
                            }

                            // adpet all foreground colours for all backgrounds
                            for (j = 0; j <= FOREGROUND_HOVER; j++)
                            {
                                if (!foreground_colour_changed[k][i][j])
                                    continue;
                                for (f = 0; f < css_fore_elements[j].length; f++)
                                {
                                    element_spec.setLength(0);
                                    if (k == 1)
                                        element_spec
                                            .append(".bodington_navigation_page ");
                                    // don't need body in navigation pages
                                    if (k == 0
                                        || !"body"
                                            .equals(css_back_elements[i][b]))
                                        element_spec
                                            .append(css_back_elements[i][b]);
                                    element_spec.append(" ");
                                    element_spec
                                        .append(css_fore_elements[j][f]);
                                    stylesheetEntry(entry.content, element_spec
                                        .toString());
                                    stylesheetProperty(entry.content, "color",
                                        preferred_foreground_colours[k][i][j]);
                                    stylesheetEndEntry(entry.content);
                                }
                            }

                        }
                    }
                }
                
                stylesheetStandardClasses(entry.content,
                    preferred_foreground_colours, preferred_background_colours); 

                // ===========================================================
                Color preferred_colour = colour_mapper_list[0][BACKGROUND]
                    .getPreferredForegroundColor(new Color(0x202020));
                if (!(new Color(0x202020)).equals(preferred_colour))
                {
                    stylesheetEntry(entry.content,
                        ".bs-logbook-command-disabled");
                    stylesheetProperty(entry.content, "color", preferred_colour);
                    stylesheetEndEntry(entry.content);
                }

                // This is currently only needed in the user stylesheet if the
                // user preference affects colour because the background lines
                // need to change colour in the CSS instructions. It is assumed
                // that resource trees are always on pages that use the
                // navigation colour scheme.

                // outputs new style CSS classes. Prefix indicates this is
                // specifically for resource menus and nothing else. Note
                // that the old style tree classes above are still there to
                // support HTML tagging that is already used in tools (perhaps
                // some 3rd party tools I don't know about) for backwards
                // compatibility. In the future more calls may be made to this
                // method to support other tree diagrams with distinct display
                // options.
                String tree_line_path = treeLinePath(req);

                // only bother outputting tree classes if they aren't
                // transparent in author stylesheet
                if ((!background_colours[1][BACKGROUND]
                    .equals(background_colours[1][BACKGROUND_MENU_1]))
                    || (!background_colours[1][BACKGROUND]
                        .equals(background_colours[1][BACKGROUND_MENU_2])))
                {
                    compileTreeStylesheet(entry.content, "res_tree",
                        tree_line_path,
                        preferred_background_colours[1][BACKGROUND_MENU_1],
                        preferred_foreground_colours[1][BACKGROUND_MENU_1],
                        tree_lines ? ("?code=" + tree_line_colour_mapper_1
                            .toString()) : null,
                        preferred_background_colours[1][BACKGROUND_MENU_2],
                        preferred_foreground_colours[1][BACKGROUND_MENU_2],
                        tree_lines ? ("?code=" + tree_line_colour_mapper_2
                            .toString()) : null, 22, res_menu_border_colour);
                    compileTreeStylesheet(entry.content, "res_tree_big",
                        tree_line_path,
                        preferred_background_colours[1][BACKGROUND_MENU_1],
                        preferred_foreground_colours[1][BACKGROUND_MENU_1],
                        tree_lines ? ("?code=" + tree_line_colour_mapper_1
                            .toString()) : null,
                        preferred_background_colours[1][BACKGROUND_MENU_2],
                        preferred_foreground_colours[1][BACKGROUND_MENU_2],
                        tree_lines ? ("?code=" + tree_line_colour_mapper_2
                            .toString()) : null, 44, res_menu_border_colour);
                }
            }
            entry.last_modified = System.currentTimeMillis();
            return entry;
        }
    }
    /**
    static final int FOREGROUND = 0;
    static final int FOREGROUND_EM = 1;
    static final int FOREGROUND_VSTRONG = 2;
    static final int FOREGROUND_LINK = 3;
    static final int FOREGROUND_VISITED = 4;
    static final int FOREGROUND_ACTIVE = 5;
    static final int FOREGROUND_HOVER = 6;

    static final int BACKGROUND = 0;
    static final int BACKGROUND_TABLE = 1;
    static final int BACKGROUND_TABLE_EM = 2;
    static final int BACKGROUND_MENU_HEAD = 3;
    static final int BACKGROUND_MENU_1 = 4;
    static final int BACKGROUND_MENU_2 = 5;
    */
    private void stylesheetStandardClasses(StringBuffer content, Color[][][] preferred_foreground_colours, Color[][] preferred_background_colours)
    {
        stylesheetEntry(content, ".fg");
        stylesheetProperty(content, "color", preferred_foreground_colours[0][BACKGROUND][FOREGROUND]);
    }

    private String treeLinePath(Request req) throws BuildingServerException
    {
        String tree_line_path = "bs_template_"; // defaults
        // to
        // be
        // relative
        // to
        // stylesheet
        // and
        // with
        // prefix
        Resource resource = req.getResource();
        String sample_tree_line = "tree_node_lines_snundnbc_22_22.gif";
        Template template = Template.get(req.getFacility().facilityname,
            resource.getImplicitHttpUIStyle(), resource.getResourceId(),
            sample_tree_line);
        if (template != null)
        {
            tree_line_path = req.getContextPath() + "/processedgif/templates"
                + template.getUrl();
            tree_line_path = tree_line_path.substring(0, tree_line_path
                .length()
                - sample_tree_line.length());
        }
        return tree_line_path;
    }

    /**
     * Data holding class that wraps up the stylesheet and a last modified date.
     */
    private class StyleSheetEntry
    {
        StringBuffer content = null;
        long last_modified = 0L;
        //int version = 0;
    }

    /**
     * The class StyleSheetSessionData.
     * <p>
     * <i>(WebLearn modification. Class made protected for use in
     * EasyBuilderFacility. 26/07/2004 Colin Tatham) </i>
     */
    protected class StyleSheetSessionData
    {
        int number_of_changes = random.nextInt() & 0x7fffffff;

        long last_changed_options = 0L;

        boolean enable_style_sheets = true;

        boolean was_unauth_style = true;
        boolean unsaved_changes = false;

        Properties preferences = new Properties();

        String getPreference(org.bodington.servlet.HttpSession http_session,
            String key) throws BuildingServerException
        {
            // try current value (in http_session)
            String value = null;
            value = preferences.getProperty(key);
            if (value != null) return value;

            // go to database through nav_session
            NavigationSession nav_session = http_session
                .getServerNavigationSession();
            // must be authenticated and a real person to use stored values
            if (nav_session == null || !nav_session.isAuthenticated()
                || nav_session.isAnonymous()) return null;
            value = nav_session.getUserProperty(key);
            if (value == null) return null;

            // if found there store in session data for later use
            preferences.setProperty(key, value);
            return value;
        }

        void setPreference(org.bodington.servlet.HttpSession http_session,
            String key, String value) throws BuildingServerException
        {
            // if found there store in session data for later use
            preferences.setProperty(key, value);

            // if real person store in database too
            NavigationSession nav_session = http_session
                .getServerNavigationSession();
            // must be authenticated and a real person to use stored values
            if (nav_session == null || !nav_session.isAuthenticated()
                || nav_session.isAnonymous())
            {
                unsaved_changes = true;
                return;
            }
            nav_session.setUserProperty(key, value);
        }

        ColourPreferenceMapper getUserColourPreferenceMapper(
            org.bodington.servlet.HttpSession http_session)
            throws BuildingServerException
        {
            String colour_option = getPreference(http_session,
                "preference.stylesheet.colour_option");
            ColourPreferenceMapper colour_mapper = new ColourPreferenceMapper();
            ColourPreferenceMapper.Adjust mapping = ColourPreferenceMapper.Adjust
                .getMapping(colour_option);
            if (mapping != null)
                colour_mapper.setType(mapping);
            return colour_mapper;
        }
    }

    /**
     * Outputs stylesheet links into the template.
     * A link to the static stylesheet is controlled by the property stylesheet.url
     */
    void stylesheet(Request breq, PrintWriter out) throws IOException,
        ServletException
    {
    	//TODO This is a temp hack.
        out.println("<link href=\"bs_virtual_auto.css\" type=\"text/css\" rel=\"stylesheet\">");

        out.print("<link type=\"text/css\" rel=\"stylesheet\" href=\"");
        out.print(breq.getContextPath());
        out.print("/static/calendar/calendar.css\">\n");
        
        // CSS for displaying OXITEMS event newsfeed content:
        out.print("<link type=\"text/css\" rel=\"stylesheet\" href=\"");
        out.print(breq.getContextPath());
        out.print("/static/events-default.css\">\n");        
    }
    
    void faviconLinks(Request breq, PrintWriter out)
    {
    	// Favicon:
        out.print("<link href=\"");
        out.print(breq.getContextPath());
        out.print("/static/favicon.ico\" rel=\"icon\" type=\"image/x-icon\">");
        out.println();
        out.print("<link href=\"");
        out.print(breq.getContextPath());
        out.print("/static/favicon.ico\" rel=\"shortcut icon\" type=\"image/x-icon\">");
        out.println();
    }

    void stylesheetEndEntry(StringBuffer buffer)
    {
        buffer.append("    }\r\n\r\n");
    }

    void stylesheetEntry(StringBuffer buffer, String entry)
    {
        buffer.append(entry);
        buffer.append("\r\n    {\r\n");
    }

    public void stylesheetField(Request req, PrintWriter writer, String name)
        throws IOException, ServletException
    {
        try
        {

            org.bodington.servlet.HttpSession http_session;
            http_session = (org.bodington.servlet.HttpSession) req
                .getSession(false);
            if (http_session == null) return;
            StyleSheetSessionData sssd = getStyleSheetSessionData(http_session);

            NavigationSession nav_session = req.getServerNavigationSession();
            if (nav_session == null) return;

            if ("size".equals(name))
            {
                String value = sssd.getPreference(http_session,
                    "preference.stylesheet.size");
                writer.println("<select name=\"size\" size=\"2\">");
                writer.print("<option ");
                if ("normal".equals(value))
                    writer.print("selected=\"selected\" ");
                writer.println("value=\"normal\">Normal Size</option>");
                writer.println("<option ");
                if ("big".equals(value))
                    writer.print("selected=\"selected\" ");
                writer.println("value=\"big\">Big Text</option>");
                writer.println("</select>");
            }

            if ("colour_option".equals(name))
            {
                String value = sssd.getPreference(http_session,
                    "preference.stylesheet.colour_option");
                writer.println("<select name=\"colour_option\" size=\"5\">");
                writer.print("<option ");
                if ("normal".equals(value))
                    writer.print("selected=\"selected\" ");
                writer.println("value=\"normal\">Designer's colours</option>");
                writer.println("<option ");
                if ("soft".equals(value))
                    writer.print("selected=\"selected\" ");
                writer
                    .println("value=\"soft\">Soften harsh colour contrasts</option>");
                writer.println("<option ");
                if ("contrast".equals(value))
                    writer.print("selected=\"selected\" ");
                writer
                    .println("value=\"contrast\">Boost contrast where necessary</option>");
                writer.println("<option ");
                if ("black".equals(value))
                    writer.print("selected=\"selected\" ");
                writer.println("value=\"black\">Black on White</option>");
                writer.println("<option ");
                if ("white".equals(value))
                    writer.print("selected=\"selected\" ");
                writer.println("value=\"white\">White on Black</option>");
                writer.println("</select>");
            }
        }
        catch (BuildingServerException bsex)
        {
            log.error("Unable to set stylesheet preference.");
            log.error(bsex.getMessage(), bsex);
            throw new ServletException(
                "Unable to display form field - technical problem.");
        }
    }

    private void stylesheetProperty(StringBuffer buffer, String name,
        Color value)
    {
        buffer.append(name);
        buffer.append(": ");
        buffer.append("#");
        buffer.append(Integer.toHexString(value.getRGB()).substring(2));
        buffer.append(";\r\n");
    }

    private void stylesheetProperty(StringBuffer buffer, String name,
        String value)
    {
        buffer.append(name);
        buffer.append(": ");
        buffer.append(value);
        buffer.append(";\r\n");
    }

    private Color[] getBackgroundColors(BuildingSession s, String style)
        throws BuildingServerException
    {
        int def = "style_navigation".equalsIgnoreCase(style) ? 1 : 0;
        Color[] c = new Color[6];
        c[BACKGROUND] = s.getPropertyColor(style + "_background_colour",
            def_back_col[def][0]);
        c[BACKGROUND_TABLE] = s.getPropertyColor(style + "_table_colour",
            def_back_col[def][1]);
        c[BACKGROUND_TABLE_EM] = s.getPropertyColor(style
            + "_table_emphasis_colour", def_back_col[def][2]);
        c[BACKGROUND_MENU_HEAD] = s.getPropertyColor(style
            + "_menu_header_colour", def_back_col[def][3]);
        c[BACKGROUND_MENU_1] = s.getPropertyColor(style
            + "_menu_highlight_colour_1", def_back_col[def][4]);
        c[BACKGROUND_MENU_2] = s.getPropertyColor(style
            + "_menu_highlight_colour_2", def_back_col[def][5]);
        return c;
    }

    /** 
     * @param s The BuildingSession so that we can get the colours. For the 
     * resource stylesheet this will be a BuildingSessionImpl for a user stylesheet
     * this will be a NavigationSession. Although at the moment all the properties
     * just fall back to the resource.
     * @param style Either style or style_navigation
     */
    private Color[] getForegroundColors(BuildingSession s, String style)
        throws BuildingServerException
    {
        int def = "style_navigation".equalsIgnoreCase(style) ? 1 : 0;
        Color[] c = new Color[7];
        c[FOREGROUND] = s.getPropertyColor(style + "_foreground_colour",
            def_fore_col[def][0]);
        c[FOREGROUND_EM] = s.getPropertyColor(style + "_emphasis_colour",
            def_fore_col[def][1]);
        c[FOREGROUND_VSTRONG] = s.getPropertyColor(style
            + "_extra_emphasis_colour", def_fore_col[def][2]);
        c[FOREGROUND_LINK] = s.getPropertyColor(style + "_link_colour",
            def_fore_col[def][3]);
        c[FOREGROUND_VISITED] = s.getPropertyColor(style
            + "_link_visited_colour", def_fore_col[def][4]);
        c[FOREGROUND_ACTIVE] = s.getPropertyColor(
            style + "_link_active_colour", def_fore_col[def][5]);
        c[FOREGROUND_HOVER] = s.getPropertyColor(style + "_link_hover_colour",
            def_fore_col[def][6]);
        return c;
    }

    /**
     * Get the style sheet session data.
     * <p>
     * <i>(WebLearn modification. Method made protected for use in
     * EasyBuilderFacility. 26/07/2004 Colin Tatham) </i>
     */
    protected StyleSheetSessionData getStyleSheetSessionData(
        org.bodington.servlet.HttpSession http_session)
    {
        StyleSheetSessionData sss_data = (StyleSheetSessionData) http_session
            .getAttribute("org.bodington.user_style_sheet_session_data");
        if (sss_data == null)
        {
            sss_data = new StyleSheetSessionData();
            http_session.setAttribute(
                "org.bodington.user_style_sheet_session_data", sss_data);
        }
        return sss_data;
    }

    public void sendAuthorStyleSheet(Request req, HttpServletResponse res)
        throws IOException, BuildingServerException
    {
        BuildingSession session = BuildingSessionManagerImpl.getSession(req
            .getResource());
        PrimaryKey resource_id = req.getResource().getResourceId();
        StyleSheetEntry entry;
        synchronized (author_style_sheets)
        {
            entry = (StyleSheetEntry) author_style_sheets.get(resource_id);
        }

        if (entry == null || entry.content == null)
            entry = compileAuthorStyleSheet(req, session, resource_id);

        else if (session.getPropertiesLastModifiedTime() > entry.last_modified)
            entry = compileAuthorStyleSheet(req, session, resource_id);

        res.setContentType("text/css");
        if (ServletUtils.isModified(req, res, entry.last_modified, 1))
        {
            res.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            return;
        }

        PrintWriter writer = new PrintWriter(res.getWriter());
        writer.print(entry.content);
    }

    public void sendAutoStyleSheet(Request req, HttpServletResponse res)
        throws IOException, BuildingServerException
    {
        org.bodington.servlet.HttpSession http_session = (org.bodington.servlet.HttpSession) req
            .getSession(false);
        Hashtable table = (Hashtable) http_session
            .getAttribute("org.bodington.user_style_sheet_auto_table");
        if (table == null)
        {
            table = new Hashtable();
            http_session.setAttribute(
                "org.bodington.user_style_sheet_auto_table", table);
        }

        Resource resource = req.getResource();
        PrimaryKey resource_id = resource.getResourceId();
        
        StyleSheetEntry entry = (StyleSheetEntry) table.get(resource_id);
        
        BuildingSession session = BuildingSessionManagerImpl.getSession(resource);

        if (entry == null || entry.content == null)
        {
        	// Need to compile a stylesheet for this resource.
        	// Because resource properties (and stylesheets) are inherited,
        	// check whether this resource or ancestor has resource properties set.
        	PrimaryKey styledResourceID = session.getStyleResourceId();
        	entry = (StyleSheetEntry) table.get(styledResourceID);
        	if (entry == null)
        	{
        		entry = new StyleSheetEntry();
        		entry.content = new StringBuffer(compileAutoStyleSheet(req, http_session));
        		entry.last_modified = System.currentTimeMillis();
        		// put styled resource entry into table held in HTTP session:
        		table.put(styledResourceID, entry);
        	}
    		// put (same) styled resource entry into table for this resource:
        	table.put(resource_id, entry);
        }
        
        else if (session.getPropertiesLastModifiedTime() > entry.last_modified)
        {
        	// need to compile new stylesheet for this resource because properties have changed:
        	entry = new StyleSheetEntry();
        	entry.content = new StringBuffer(compileAutoStyleSheet(req, http_session));
        	entry.last_modified = System.currentTimeMillis();
        	// add to table held in HTTP session:
        	table.put(resource_id, entry);
        }

        res.setContentType("text/css");
        res.setHeader("Cache-Control", "no-cache");
        res.setHeader("Pragma", "no-cache");
        res.setDateHeader("Last-Modified", entry.last_modified);
        res.setDateHeader("Expires", entry.last_modified);

        PrintWriter writer = new PrintWriter(res.getWriter());
        writer.print(entry.content);
    }

    public void sendUserStyleSheet(org.bodington.servlet.Request req,
        HttpServletResponse res) throws IOException, BuildingServerException
    {
        org.bodington.servlet.HttpSession http_session = (org.bodington.servlet.HttpSession) req
            .getSession(false);

        StyleSheetSessionData sss_data = getStyleSheetSessionData(http_session);

        Hashtable user_style_sheets = (Hashtable) http_session
            .getAttribute("org.bodington.user_style_sheet_table");
        if (user_style_sheets == null)
        {
            user_style_sheets = new Hashtable();
            http_session.setAttribute("org.bodington.user_style_sheet_table",
                user_style_sheets);
        }

        StyleSheetEntry entry = (StyleSheetEntry) user_style_sheets.get(req
            .getResource().getResourceId());

        // compile if there isn't a style sheet entry in the table,
        // if there is any content in the entry or if the user changed
        // options since the content was last compiled
        if (entry == null || entry.content == null
            || entry.last_modified < sss_data.last_changed_options)
        {
            PrimaryKey user_id = req.getUserId();
            entry = compileUserStyleSheet(req, http_session, sss_data, user_id,
                entry);
            user_style_sheets.put(req.getResource().getResourceId(), entry);
        }
        res.setContentType("text/css");
        if (ServletUtils.isModified(req, res, entry.last_modified, 1))
        {
            res.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            return;
        }

        PrintWriter writer = new PrintWriter(res.getWriter());
        writer.print(entry.content);
    }

    public void processStylesheetPreferences(org.bodington.servlet.Request req)
    {
        try
        {
            org.bodington.servlet.HttpSession http_session;
            http_session = (org.bodington.servlet.HttpSession) req
                .getSession(false);
            if (http_session == null) return;

            StyleSheetSessionData sss_data = this
                .getStyleSheetSessionData(http_session);

            NavigationSession nav_session = req.getServerNavigationSession();
            if (nav_session == null) return;

            String size = req.getParameter("size");
            String colour_option = req.getParameter("colour_option");

            if (size == null && colour_option == null) return;

            // store in part of http session and perhaps in database too
            if (size != null)
                sss_data.setPreference(http_session,
                    "preference.stylesheet.size", size);
            if (colour_option != null)
                sss_data.setPreference(http_session,
                    "preference.stylesheet.colour_option", colour_option);
            sss_data.last_changed_options = System.currentTimeMillis();
            sss_data.number_of_changes++;
            http_session
                .removeAttribute("org.bodington.user_style_sheet_auto_table");
        }
        catch (Exception e)
        {
            log.error("Unable to set stylesheet preference.");
            log.error(e.getMessage(), e);
        }
    }

    public void enableStylesheets(Request breq, boolean enable)
        throws ServletException
    {
        org.bodington.servlet.HttpSession http_session = (org.bodington.servlet.HttpSession) breq
            .getSession(false);
        if (http_session == null) return;

        StyleSheetSessionData sss_data = this
            .getStyleSheetSessionData(http_session);

        sss_data.enable_style_sheets = enable;

    }

    public void loginStyleSheetPreferences(Request request)
    {
        org.bodington.servlet.HttpSession http_session = (org.bodington.servlet.HttpSession) request
            .getSession(false);
        if (http_session == null) return;

        StyleSheetSessionData sss_data = this
            .getStyleSheetSessionData(http_session);

        String pref = request
            .getParameter("org_bodington_servlet_style_sheet_pref");
        if (pref == null || pref.length() == 0) return;
        String param = request.getParameter("org_bodington_servlet_big");

        if (param != null && param.length() > 0)
            try
            {
                sss_data.setPreference(http_session,
                    "preference.stylesheet.size", "big");
            }
            catch (Exception e)
            {
                log.error(e.getMessage(), e);
            }

        sss_data.last_changed_options = System.currentTimeMillis();
        sss_data.number_of_changes++;
        http_session
            .removeAttribute("org.bodington.user_style_sheet_auto_table");
    }

    public String getTemplateGifColourMapperCode(Request breq,
        String html_body_class)
    {
        String colour_mapper_code = "";

        // style sheet data stored in http session has user's colour preferences
        org.bodington.servlet.HttpSession http_session = (org.bodington.servlet.HttpSession) breq
            .getSession(false);
        BuildingSession building_session = breq.getBuildingSession();

        try
        {
            StyleSheetSessionData sss_data = this
                .getStyleSheetSessionData(http_session);
            ColourPreferenceMapper colour_mapper = sss_data
                .getUserColourPreferenceMapper(http_session);

            // get the id of the resource for which style is defined
            // (i.e. current resource or ancestor of current resource)
            Color f;
            Color b;
            if ("bodington_navigation_page".equals(html_body_class))
            {
                f = building_session.getPropertyColor(
                    "style_navigation_graphic_foreground_colour", new Color(
                        0xeeeeee));
                b = building_session.getPropertyColor(
                    "style_navigation_graphic_background_colour", new Color(
                        0x111111));
            }
            else
            {
                f = building_session.getPropertyColor(
                    "style_graphic_foreground_colour", new Color(0xeeeeee));
                b = building_session.getPropertyColor(
                    "style_graphic_background_colour", new Color(0x111111));
            }
            colour_mapper.setReferenceColours(b, f);
            colour_mapper_code = "?code=" + colour_mapper.toString();
        }
        catch (Exception ex)
        {
            log.error(ex.getMessage(), ex);
        }

        return colour_mapper_code;
    }

    //uhi:awc get size and colour preferences
    /**
     * Rturns size and colour preferences uhi:awc
     * @param req The Building HTTP request.
     * @param key The
     * @return the current authenticated username or NULL if user is anonymous
     *               or has not been authenticated
     */
    public String getSessionDataPreference(Request req, String key)
    {
        String value;
        StyleSheetSessionData sss_data;
        org.bodington.servlet.HttpSession http_session;

        http_session = (org.bodington.servlet.HttpSession) req
            .getSession(false);
        if (http_session == null) return null;

        sss_data = this.getStyleSheetSessionData(http_session);
        try
        {
            value = sss_data.getPreference(http_session, key);
        }
        catch (Exception e)
        {
            return null;
        }
        return value;
    }

}
