/* ======================================================================
The Bodington System Software License, Version 1.0
  
Copyright (c) 2001 The University of Leeds.  All rights reserved.
  
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

1.  Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2.  Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

3.  The end-user documentation included with the redistribution, if any,
must include the following acknowledgement:  "This product includes
software developed by the University of Leeds
(http://www.bodington.org/)."  Alternately, this acknowledgement may
appear in the software itself, if and wherever such third-party
acknowledgements normally appear.

4.  The names "Bodington", "Nathan Bodington", "Bodington System",
"Bodington Open Source Project", and "The University of Leeds" must not be
used to endorse or promote products derived from this software without
prior written permission. For written permission, please contact
d.gardner@leeds.ac.uk.

5.  The name "Bodington" may not appear in the name of products derived
from this software without prior written permission of the University of
Leeds.

THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO,  TITLE,  THE IMPLIED WARRANTIES 
OF QUALITY  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO 
EVENT SHALL THE UNIVERSITY OF LEEDS OR ITS CONTRIBUTORS BE LIABLE FOR 
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
POSSIBILITY OF SUCH DAMAGE.
=========================================================

This software was originally created by the University of Leeds and may contain voluntary 
contributions from others.  For more information on the Bodington Open Source Project, please 
see http://bodington.org/

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


package org.bodington.servlet.facilities;

import java.awt.Color;
import java.io.IOException;
import java.io.PrintWriter;
import java.rmi.RemoteException;
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)}};

    private StyleSheetEntry compileAuthorStyleSheet(Request req,
        BuildingSession session, PrimaryKey resource_id)
        throws RemoteException, 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_EM]);
            stylesheetEndEntry(entry.content);

            stylesheetEntry(entry.content,
                ".bodington_navigation_page .bs-table-acl");
            stylesheetProperty(entry.content, "background-color",
                nav_background_colours[BACKGROUND_TABLE_EM]);
            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-bottom", "+1ex");
            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 line");
            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);

            //  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,
        RemoteException, 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();
    }

    private StringBuffer compileTreeStylesheet(StringBuffer buffer, // This
        // receives
        // the
        // CSS
        // definitions
        String prefix, // This prefix is used on all class names
        String lines_path, // Used as path to GIFs
        Color background_1, // Color for highlight 1
        Color[] foreground_1, String lines_colour_mapper_code_1, // Used
        // on
        // all
        // references
        // to
        // background
        // GIFs
        // for
        // colour
        // adaptation
        Color background_2, // Color for highlight 1
        Color[] foreground_2, String lines_colour_mapper_code_2, // Used
        // on
        // all
        // references
        // to
        // background
        // GIFs
        // for
        // colour
        // adaptation
        int icon_size, // dimensions of icons - affects dimenstions and
        // background images
        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");
        //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");
        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;
    }

    private StyleSheetEntry compileUserStyleSheet(Request req,
        org.bodington.servlet.HttpSession http_session,
        StyleSheetSessionData sss_data, PrimaryKey user_id,
        StyleSheetEntry entry) throws RemoteException, 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]);
                        if (colour_option.equals("black"))
                            colour_mapper_list[k][i]
                                .setType(ColourPreferenceMapper.TYPE_BLACK_ON_WHITE);
                        else if (colour_option.equals("white"))
                            colour_mapper_list[k][i]
                                .setType(ColourPreferenceMapper.TYPE_WHITE_ON_BLACK);
                        else if (colour_option.equals("contrast"))
                            colour_mapper_list[k][i]
                                .setType(ColourPreferenceMapper.TYPE_CONTRAST);
                        else if (colour_option.equals("soft"))
                            colour_mapper_list[k][i]
                                .setType(ColourPreferenceMapper.TYPE_SOFT);
                        else
                            colour_mapper_list[k][i]
                                .setType(ColourPreferenceMapper.TYPE_NORMAL);
                    }
                }

                // 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);
                                }
                            }

                        }
                    }
                }

                // ===========================================================
                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;
        }
    }

    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;
    }

    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, RemoteException
        {
            // 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,
            RemoteException
        {
            // 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, RemoteException
        {
            String colour_option = getPreference(http_session,
                "preference.stylesheet.colour_option");
            if (colour_option == null) colour_option = "normal";
            ColourPreferenceMapper colour_mapper = new ColourPreferenceMapper();

            if (colour_option.equals("black"))
                colour_mapper
                    .setType(ColourPreferenceMapper.TYPE_BLACK_ON_WHITE);
            else if (colour_option.equals("white"))
                colour_mapper
                    .setType(ColourPreferenceMapper.TYPE_WHITE_ON_BLACK);
            else if (colour_option.equals("contrast"))
                colour_mapper.setType(ColourPreferenceMapper.TYPE_CONTRAST);
            else if (colour_option.equals("soft"))
                colour_mapper.setType(ColourPreferenceMapper.TYPE_SOFT);
            else
                colour_mapper.setType(ColourPreferenceMapper.TYPE_NORMAL);

            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 href=\"");
        out.print(BuildingServer.getInstance().getProperty("stylesheet.url", "/extra.css"));
        out.println("\" type=\"text/css\" rel=\"stylesheet\">");
    }

    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, RemoteException
    {
        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;
    }

    private Color[] getForegroundColors(BuildingSession s, String style)
        throws BuildingServerException, RemoteException
    {
        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, RemoteException, 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);

        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(Response.SC_NOT_MODIFIED);
            return;
        }

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

    public void sendAutoStyleSheet(Request req, HttpServletResponse res)
        throws IOException, RemoteException, 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);
        }

        String content = (String) table.get(req.getResource().getResourceId());

        if (content == null)
        {
            BuildingSession session = BuildingSessionManagerImpl.getSession(req
                .getResource());
            PrimaryKey rid = session.getStyleResourceId();
            content = (String) table.get(rid);
            if (content == null)
            {
                content = compileAutoStyleSheet(req, http_session);
                table.put(rid, content);
            }
            table.put(req.getResource().getResourceId(), content);
        }

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

        PrintWriter writer = new PrintWriter(res.getWriter());
        writer.print(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(entry, req.getResource().getResourceId());
        }
        res.setContentType("text/css");
        if (ServletUtils.isModified(req, res, entry.last_modified, 1))
        {
            res.setStatus(Response.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;
    }

}