/*
 * Copyright The Sett Ltd, 2005 to 2014.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.thesett.aima.attribute.time;

import java.nio.ByteBuffer;

/**
 * TimeUtils provides static methods for performing time related calculations, particularly conversions between unix
 * timestamps and dates and times.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Perform conversions to and from times and dates in milliseconds and ASCII formats.
 *
 Write times and dates to byte buffers.
 *

 *
 * @author Rupert Smith
 */
public class TimeUtils
{
    /** The number of milliseconds in a second. */
    private static final int MILLIS_PER_SECOND = 1000;

    /** The number of milliseconds in a minute. */
    public static final int MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;

    /** The number of milliseconds in an hour. */
    public static final int MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;

    /** The number of milliseconds in a day. */
    private static final int MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR;

    /** The number of milliseconds in a year. */
    public static final long MILLIS_PER_YEAR = 365L * MILLIS_PER_DAY;

    /** The number of milliseconds in a leap year. */
    public static final long MILLIS_PER_LEAP_YEAR = 366L * MILLIS_PER_DAY;

    /** The number of ticks in a minute. */
    public static final long TICKS_PER_MINUTE = MILLIS_PER_MINUTE;

    /** The number of milliseconds in 365.2425 days. */
    private static final long MILLIS_PER_REAL_YEAR = (long) (365.2425 * MILLIS_PER_DAY);

    /** The number of days from 0000 to 1970. */
    private static final int DAYS_TO_1970 = 719527;

    /** Tables the number of days in the year prior to the start of each month, for leap years. */
    private static final int[] LEAP_DAYS_IN_YEAR_PRIOR_TO_MONTH =
        { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 };

    /** Tables the number of days in the year prior to the start of each month. */
    private static final int[] USUAL_DAYS_IN_YEAR_PRIOR_TO_MONTH =
        { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };

    /** Tables the number of days in each month, for leap years. */
    private static final int[] LEAP_DAYS_IN_MONTH = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

    /** Tables the number of days in each month, for non leap years. */
    private static final int[] USUAL_DAYS_IN_MONTH = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

    /** The number of milliseconds per day divided by 1024. */
    private static final long MILLIS_PER_DAY_OVER_1024 = MILLIS_PER_DAY >> 10;

    /** The length of a date only in YYYYMMDD format. */
    public static final int DATE_ONLY_LENGTH = 8;

    /** The length of a timestamp in YYYYMMDD-HH:MM:SS format. */
    public static final int DATE_TIME_WITHOUT_MILLISECONDS_LENGTH = 17;

    /** The length of a timestamp in YYYYMMDD-HH:MM:SS.sss format. */
    public static final int DATE_TIME_WITH_MILLISECONDS_LENGTH = 21;

    /** The length of a time of day in HH:MM:SS format. */
    public static final int TIME_ONLY_LENGTH_WIHTOUT_MILLISECONDS = 8;

    /** The length of a time of day in HH:MM:SS.sss format. */
    public static final int TIME_ONLY_LENGTH_WITH_MILLISECONDS = 12;

    /** The length of a time zone offset in [+/-]HH format. */
    public static final int TIME_ZONE_WITHOUT_MINUTES_LENGTH = 3;

    /** The length of a time zone offset in [+/-]HH:MM format. */
    public static final int TIME_ZONE_WITH_MINUTES_LENGTH = 6;

    /**
     * Converts a field by field timestamp into millisecond ticks.
     *
     * @param  year         The year.
     * @param  month        The month.
     * @param  day          The day.
     * @param  hours        The hours.
     * @param  minutes      The minutes.
     * @param  seconds      The seconds.
     * @param  milliseconds The milliseconds.
     *
     * @return Millisecond ticks since midnight 01/01/1970.
     */
    public static long timestampToTicks(int year, int month, int day, int hours, int minutes, int seconds,
        int milliseconds)
    {
        boolean isLeapYear = isLeapYear(year);

        long dayComponent = (long) (day - 1) * MILLIS_PER_DAY;
        long monthComponent = millisToStartOfMonth(month, isLeapYear);
        long yearComponent = millisToYearStart(year);
        long hoursComponent = (long) hours * MILLIS_PER_HOUR;
        long minutesComponent = (long) minutes * MILLIS_PER_MINUTE;
        long secondsComponent = (long) seconds * MILLIS_PER_SECOND;

        return dayComponent + monthComponent + yearComponent + hoursComponent + minutesComponent + secondsComponent +
            milliseconds;
    }

    /**
     * Converts a field by field timestamp into millisecond ticks.
     *
     * @param  year    The year.
     * @param  month   The month.
     * @param  day     The day.
     * @param  hours   The hours.
     * @param  minutes The minutes.
     * @param  seconds The seconds.
     *
     * @return Millisecond ticks since midnight 01/01/1970.
     */
    public static long timestampToTicks(int year, int month, int day, int hours, int minutes, int seconds)
    {
        boolean isLeapYear = isLeapYear(year);

        long dayComponent = (long) (day - 1) * MILLIS_PER_DAY;
        long monthComponent = millisToStartOfMonth(month, isLeapYear);
        long yearComponent = millisToYearStart(year);
        long hoursComponent = (long) hours * MILLIS_PER_HOUR;
        long minutesComponent = (long) minutes * MILLIS_PER_MINUTE;
        long secondsComponent = (long) seconds * MILLIS_PER_SECOND;

        return dayComponent + monthComponent + yearComponent + hoursComponent + minutesComponent + secondsComponent;
    }

    /**
     * Converts a field by field timestamp into millisecond ticks.
     *
     * @param  year  The year.
     * @param  month The month.
     * @param  day   The day.
     *
     * @return Millisecond ticks since midnight 01/01/1970.
     */
    public static long timestampToTicks(int year, int month, int day)
    {
        boolean isLeapYear = isLeapYear(year);

        long dayComponent = (long) (day - 1) * MILLIS_PER_DAY;
        long monthComponent = millisToStartOfMonth(month, isLeapYear);
        long yearComponent = millisToYearStart(year);

        return dayComponent + monthComponent + yearComponent;
    }

    /**
     * Converts a field by field time of day into millisecond ticks.
     *
     * @param  hour        The hours since midnight.
     * @param  minute      The minutes.
     * @param  second      The seconds.
     * @param  millisecond The milliseconds.
     *
     * @return Millisecond ticks since midnight.
     */
    public static long timeOfDayToTicks(int hour, int minute, int second, int millisecond)
    {
        return millisecond + (MILLIS_PER_SECOND * second) + (MILLIS_PER_MINUTE * minute) + (MILLIS_PER_HOUR * hour);
    }

    /**
     * Converts a field by field time of day into millisecond ticks.
     *
     * @param  hour   The hours since midnight.
     * @param  minute The minutes.
     *
     * @return Millisecond ticks since midnight.
     */
    public static long timeOfDayToTicks(int hour, int minute)
    {
        return (MILLIS_PER_MINUTE * minute) + (MILLIS_PER_HOUR * hour);
    }

    /**
     * Extracts the years component of a time in millisecond ticks.
     *
     * @param  ticks The time in ticks.
     *
     * @return The years component of the time.
     */
    public static int ticksToYears(long ticks)
    {
        // The number of years is ticks floor divided by number of milliseconds in 365 1/4 days.
        //return flooredDiv(ticks, MILLIS_PER_REAL_YEAR) + 1970;
        //return flooredDiv(ticks + ((long)DAYS_TO_1970 * MILLIS_PER_DAY), MILLIS_PER_REAL_YEAR);

        long unitMillis = MILLIS_PER_YEAR / 2;
        long i2 = (ticks >> 1) + ((1970L * MILLIS_PER_YEAR) / 2);

        if (i2 < 0)
        {
            i2 = i2 - unitMillis + 1;
        }

        int year = (int) (i2 / unitMillis);

        long yearStart = millisToYearStart(year);
        long diff = ticks - yearStart;

        if (diff < 0)
        {
            year--;
        }
        else if (diff >= (MILLIS_PER_DAY * 365L))
        {
            // One year may need to be added to fix estimate.
            long oneYear;

            if (isLeapYear(year))
            {
                oneYear = MILLIS_PER_DAY * 366L;
            }
            else
            {
                oneYear = MILLIS_PER_DAY * 365L;
            }

            yearStart += oneYear;

            if (yearStart <= ticks)
            {
                year++;
            }
        }

        return year;
    }

    /**
     * Extracts the months component of a time in millisecond ticks.
     *
     * @param  ticks The time in ticks.
     *
     * @return The months component of the time.
     */
    public static int ticksToMonths(long ticks)
    {
        return getMonthOfYear(ticks, ticksToYears(ticks));
    }

    /**
     * Extracts the date (day in month) component of a time in millisecond ticks.
     *
     * @param  ticks The time in ticks.
     *
     * @return The date (day in month) component of the time.
     */
    public static int ticksToDate(long ticks)
    {
        int year = ticksToYears(ticks);
        int month = ticksToMonths(ticks);

        long dayOffset = ticks;
        dayOffset -= millisToYearStart(year);
        dayOffset -=
            isLeapYear(year) ? ((long) LEAP_DAYS_IN_YEAR_PRIOR_TO_MONTH[month - 1] * MILLIS_PER_DAY)
                             : ((long) USUAL_DAYS_IN_YEAR_PRIOR_TO_MONTH[month - 1] * MILLIS_PER_DAY);

        return (int) (dayOffset / MILLIS_PER_DAY) + 1;
    }

    /**
     * Extracts the hours component of a time in millisecond ticks.
     *
     * @param  ticks The time in ticks.
     *
     * @return The hours component of the time.
     */
    public static int ticksToHours(long ticks)
    {
        return (int) (ticks % MILLIS_PER_DAY) / MILLIS_PER_HOUR;
    }

    /**
     * Extracts the minutes component of a time in millisecond ticks.
     *
     * @param  ticks The time in ticks.
     *
     * @return The minutes component of the time.
     */
    public static int ticksToMinutes(long ticks)
    {
        return (int) (ticks % MILLIS_PER_HOUR) / MILLIS_PER_MINUTE;
    }

    /**
     * Extracts the seconds component of a time in millisecond ticks.
     *
     * @param  ticks The time in ticks.
     *
     * @return The seconds component of the time.
     */
    public static int ticksToSeconds(long ticks)
    {
        return (int) (ticks % MILLIS_PER_MINUTE) / MILLIS_PER_SECOND;
    }

    /**
     * Extracts the milliseconds component of a time in millisecond ticks.
     *
     * @param  ticks The time in ticks.
     *
     * @return The milliseconds component of the time.
     */
    public static int ticksToMilliseconds(long ticks)
    {
        return (int) (ticks % MILLIS_PER_SECOND);
    }

    /**
     * Sets the component of the timestamp, returning the new timestamp with updated component.
     *
     * @param  ticks The time in ticks.
     * @param  hours The new hours field.
     *
     * @return The new time in ticks.
     */
    public static long ticksWithHoursSetTo(long ticks, int hours)
    {
        long oldHours = ticksToHours(ticks);

        return ticks - (oldHours * MILLIS_PER_HOUR) + (hours * MILLIS_PER_HOUR);
    }

    /**
     * Sets the minutes component of the timestamp, returning the new timestamp with updated component.
     *
     * @param  ticks   The time in ticks.
     * @param  minutes The new minutes field.
     *
     * @return The new time in ticks.
     */
    public static long ticksWithMinutesSetTo(long ticks, int minutes)
    {
        long oldMinutes = ticksToMinutes(ticks);

        return ticks - (oldMinutes * MILLIS_PER_MINUTE) + (minutes * MILLIS_PER_MINUTE);
    }

    /**
     * Sets the seconds component of the timestamp, returning the new timestamp with updated component.
     *
     * @param  ticks   The time in ticks.
     * @param  seconds The new seconds field.
     *
     * @return The new time in ticks.
     */
    public static long ticksWithSecondsSetTo(long ticks, int seconds)
    {
        long oldSeconds = ticksToSeconds(ticks);

        return ticks - (oldSeconds * MILLIS_PER_SECOND) + (seconds * MILLIS_PER_SECOND);
    }

    /**
     * Sets the milliseconds component of the timestamp, returning the new timestamp with updated component.
     *
     * @param  ticks        The time in ticks.
     * @param  milliseconds The new milliseconds field.
     *
     * @return The new time in ticks.
     */
    public static long ticksWithMillisecondsSetTo(long ticks, int milliseconds)
    {
        long oldMillis = ticksToMilliseconds(ticks);

        return ticks - oldMillis + milliseconds;
    }

    /**
     * Sets the year component of the timestamp, returning the new timestamp with updated component.
     *
     * @param  ticks The time in ticks.
     * @param  year  The new year field.
     *
     * @return The new time in ticks.
     */
    public static long ticksWithYearSetTo(long ticks, int year)
    {
        int oldYear = ticksToYears(ticks);

        return ticks - millisToYearStart(oldYear) + millisToYearStart(year);
    }

    /**
     * Sets the month component of the timestamp, returning the new timestamp with updated component.
     *
     * @param  ticks The time in ticks.
     * @param  month The new month field.
     *
     * @return The new time in ticks.
     */
    public static long ticksWithMonthSetTo(long ticks, int month)
    {
        int year = ticksToYears(ticks);
        boolean isLeapYear = isLeapYear(year);
        int oldMonth = ticksToMonths(ticks);

        return ticks - millisToStartOfMonth(oldMonth, isLeapYear) + millisToStartOfMonth(month, isLeapYear);
    }

    /**
     * Sets the date component of the timestamp, returning the new timestamp with updated component.
     *
     * @param  ticks The time in ticks.
     * @param  date  The new date field.
     *
     * @return The new time in ticks.
     */
    public static long ticksWithDateSetTo(long ticks, int date)
    {
        int oldDays = ticksToDate(ticks);

        return ticks - (oldDays * MILLIS_PER_DAY) + (date * MILLIS_PER_DAY);
    }

    /**
     * Calculates the number of milliseconds to the start of the specified year, taking 1970 as zero.
     *
     * @param  year The year.
     *
     * @return The number of milliseconds to the start of the specified year, taking 1970 as zero.
     */
    public static long millisToYearStart(int year)
    {
        // Calculate how many leap years elapsed prior to the year in question.
        int leapYears = year / 100;

        if (year < 0)
        {
            leapYears = ((year + 3) >> 2) - leapYears + ((leapYears + 3) >> 2) - 1;
        }
        else
        {
            leapYears = (year >> 2) - leapYears + (leapYears >> 2);

            if (isLeapYear(year))
            {
                leapYears--;
            }
        }

        return ((year * 365L) + leapYears - DAYS_TO_1970) * MILLIS_PER_DAY;
    }

    /**
     * Writes a time of day with milliseconds in the format HH:MM:SS[.sss], into a byte buffer. The specified buffer
     * will be enlarged if necessary using the specified byte buffer pool.
     *
     * @param  buffer The initial buffer to write to.
     * @param  value  The time of day since midnight, in milliseconds.
     *
     * @return The buffer written to.
     */
    public static ByteBuffer putTimeOnlyAsString(ByteBuffer buffer, long value)
    {
        buffer = putTimeOnlyAsString(buffer, value, true);

        return buffer;
    }

    /**
     * Calculate the maximum number of ASCII characters needed to represent a time zone, with or without minutes.
     *
     * @param  offset         The time zone offset.
     * @param  includeMinutes <tt>true</tt> to include minutes.
     *
     * @return The maximum number of ASCII characters needed to represent the time zone.
     */
    public static int getCharacterCountTimeZone(long offset, boolean includeMinutes)
    {
        if (offset == 0)
        {
            return 1;
        }
        else if (includeMinutes)
        {
            return TIME_ZONE_WITH_MINUTES_LENGTH;
        }
        else
        {
            return TIME_ZONE_WITHOUT_MINUTES_LENGTH;
        }
    }

    /**
     * Writes only the time component of a time of day timestamp to a byte array in the following format:
     * HH:MM:SS[.sss]. The millisecond value is optional and is only written if requested. If there is insufficient
     * space in the buffer to write the value into, then the buffer size is increased using the supplied byte buffer
     * pool.
     *
     * @param  buffer              The byte buffer to write to.
     * @param  value               The value to write as a millisecond timestamp.
     * @param  includeMilliseconds <tt>true</tt> to include milliseconds in the output.
     *
     * @return The byte buffer with the value written to it. This may be a different buffer to the one passed in if the
     *         buffer had to be copied in order to increase its size.
     */
    public static ByteBuffer putTimeOnlyAsString(ByteBuffer buffer, long value, boolean includeMilliseconds)
    {
        // Ensure there is sufficient space in the buffer for the date.
        int charsRequired =
            includeMilliseconds ? TIME_ONLY_LENGTH_WITH_MILLISECONDS : TIME_ONLY_LENGTH_WIHTOUT_MILLISECONDS;

        buffer = ByteBufferUtils.putPaddedInt32AsString(buffer, ticksToHours(value), 2);
        buffer = ByteBufferUtils.putByteAsString(buffer, (byte) ':');
        buffer = ByteBufferUtils.putPaddedInt32AsString(buffer, ticksToMinutes(value), 2);
        buffer = ByteBufferUtils.putByteAsString(buffer, (byte) ':');
        buffer = ByteBufferUtils.putPaddedInt32AsString(buffer, ticksToSeconds(value), 2);

        if (includeMilliseconds)
        {
            buffer = ByteBufferUtils.putByteAsString(buffer, (byte) '.');
            buffer = ByteBufferUtils.putPaddedInt32AsString(buffer, ticksToMilliseconds(value), 3);
        }

        return buffer;
    }

    /**
     * Determines whether or not a year is a leap year. This takes no account of when leap years were first introduced,
     * so will not be accurate for ancient dates.
     *
     * @param  year The year.
     *
     * @return <tt>true</tt> if the year is a leap year.
     */
    private static boolean isLeapYear(int year)
    {
        if ((year & 3) != 0)
        {
            return false;
        }

        if ((year % 100) != 0)
        {
            return true;
        }

        return (year % 400) == 0;
    }

    /**
     * Given a millisecond timestamp that lands in a specified year, calculate what month the timestamp corresponds to.
     *
     * @param  ticks The timestamp.
     * @param  year  The year that the timestamp falls in.
     *
     * @return The month that the timestamp falls in, in the range 1 to 12.
     */
    private static int getMonthOfYear(long ticks, int year)
    {
        int i = (int) ((ticks - millisToYearStart(year)) >> 10);

        return (isLeapYear(year))
            ? ((i < (182 * MILLIS_PER_DAY_OVER_1024))
                ? ((i < (91 * MILLIS_PER_DAY_OVER_1024))
                    ? ((i < (31 * MILLIS_PER_DAY_OVER_1024)) ? 1 : ((i < (60 * MILLIS_PER_DAY_OVER_1024)) ? 2 : 3))
                    : ((i < (121 * MILLIS_PER_DAY_OVER_1024)) ? 4 : ((i < (152 * MILLIS_PER_DAY_OVER_1024)) ? 5 : 6)))
                : ((i < (274 * MILLIS_PER_DAY_OVER_1024))
                    ? ((i < (213 * MILLIS_PER_DAY_OVER_1024)) ? 7 : ((i < (244 * MILLIS_PER_DAY_OVER_1024)) ? 8 : 9))
                    : ((i < (305 * MILLIS_PER_DAY_OVER_1024)) ? 10
                                                              : ((i < (335 * MILLIS_PER_DAY_OVER_1024)) ? 11 : 12))))
            : ((i < (181 * MILLIS_PER_DAY_OVER_1024))
                ? ((i < (90 * MILLIS_PER_DAY_OVER_1024))
                    ? ((i < (31 * MILLIS_PER_DAY_OVER_1024)) ? 1 : ((i < (59 * MILLIS_PER_DAY_OVER_1024)) ? 2 : 3))
                    : ((i < (120 * MILLIS_PER_DAY_OVER_1024)) ? 4 : ((i < (151 * MILLIS_PER_DAY_OVER_1024)) ? 5 : 6)))
                : ((i < (273 * MILLIS_PER_DAY_OVER_1024))
                    ? ((i < (212 * MILLIS_PER_DAY_OVER_1024)) ? 7 : ((i < (243 * MILLIS_PER_DAY_OVER_1024)) ? 8 : 9))
                    : ((i < (304 * MILLIS_PER_DAY_OVER_1024)) ? 10
                                                              : ((i < (334 * MILLIS_PER_DAY_OVER_1024)) ? 11 : 12))));
    }

    /**
     * Given a month, numbered from 1 to 12, calculates the number of milliseconds to the start of that month in a year.
     *
     * @param  month    The month.
     * @param  leapYear <tt>true</tt> if the year is a leap year.
     *
     * @return The number of milliseconds to the start of the month.
     */
    private static long millisToStartOfMonth(int month, boolean leapYear)
    {
        return
            (long) (leapYear ? LEAP_DAYS_IN_YEAR_PRIOR_TO_MONTH[month - 1]
                             : USUAL_DAYS_IN_YEAR_PRIOR_TO_MONTH[month - 1]) * MILLIS_PER_DAY;
    }
}


/*
 * Copyright The Sett Ltd, 2005 to 2014.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.thesett.common.io;

import java.io.IOException;
import java.io.InputStream;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

/**
 * DistributedInputStreamImpl provides a wrapper mechanism for input stream that allows them to be treated as remote
 * objects. It implements the remote version of the InputStream API provides by {@link DistributedInputStream}. Its
 * constructor takes an input stream as an argument and the rest of the methods are simply wrapper methods that delegate
 * their action to the wrapped input stream.
 *
 *
A client proxy to the distributed input stream is implemented by {@link ClientInputStream}. This class is an
 * extension of {@link java.io.InputStream} and is serializable. This allows input streams to passed and returned as
 * arguments to RMI calls and for other classes that work with input streams to use them remotely without being aware of
 * the remote nature of the input stream.
 *
 *

CRC Card

 *
ResponsibilitiesCollaborations
 *
Provide RMI wrapper to input stream.  {@link java.io.InputStream}
 *

 *
 * @author Rupert Smith
 * @todo   Remote input streams will not work efficiently if they are read a byte at a time for large amounts of data.
 *         Existing code may therefore not work well with simple distributed input streams. If this is a problem a
 *         caching/buffered distributed input stream can be written that gets its input in chunks and caches these in
 *         the client proxy and then feeds the byte at a time reader methods from this cache. The chunk size can be set
 *         as a parameter allowing an optimal chunk size to be chosen to fit different purposes.
 */
public class DistributedInputStreamImpl extends UnicastRemoteObject implements DistributedInputStream
{
    /** The local input stream that serves as the source for distributed one. */
    private InputStream source;

    /**
     * Builds a distrubted input stream that wraps a local one.
     *
     * @param  src the local source input stream.
     *
     * @throws RemoteException If an RMI error occurs during remote access.
     */
    public DistributedInputStreamImpl(InputStream src) throws RemoteException
    {
        super();
        source = src;
    }

    /**
     * Returns the number of bytes that can be read (or skipped over) from this input stream without blocking by the
     * next caller of a method for this input stream.
     *
     * @return The number of bytes that can be read from this input stream without blocking.
     *
     * @throws IOException     If an I/O error occurs when accessing the stream.
     * @throws RemoteException If an RMI error occurs during remote access.
     */
    public int available() throws IOException
    {
        return source.available();
    }

    /**
     * Closes this input stream and releases any system resources associated with the stream.
     *
     * @throws IOException     If an I/O error occurs when accessing the stream.
     * @throws RemoteException If an RMI error occurs during remote access.
     */
    public void close() throws IOException
    {
        source.close();
    }

    /**
     * Marks the current position in this input stream.
     *
     * @param  readlimit The maximum limit of bytes that can be read before the mark position becomes invalid.
     *
     * @throws RemoteException If an RMI error occurs during remote access.
     */
    public void mark(int readlimit) throws RemoteException
    {
        source.mark(readlimit);
    }

    /**
     * Tests if this input stream supports the mark and reset methods.
     *
     * @return If this stream instance supports the mark and reset methods; false otherwise.
     *
     * @throws RemoteException If an RMI error occurs during remote access.
     */
    public boolean markSupported() throws RemoteException
    {
        return source.markSupported();
    }

    /**
     * Reads the next byte of data from the input stream.
     *
     * @return The next byte of data, or -1 if the end of the stream is reached.
     *
     * @throws IOException     If an I/O error occurs when accessing the stream.
     * @throws RemoteException If an RMI error occurs during remote access.
     */
    public int read() throws IOException
    {
        return source.read();
    }

    /**
     * Reads some number of bytes from the input stream and stores them into the buffer array b. The bytes are also
     * returned wrapped in a byte block so that they can be returnd over RMI.
     *
     * @param  b The byte array to store the results of the read into.
     *
     * @return The same results but wrapped in byte block.
     *
     * @throws IOException     If an I/O error occurs when accessing the stream.
     * @throws RemoteException If an RMI error occurs during remote access.
     */
    public ByteBlock read(byte[] b) throws IOException
    {
        int count = source.read(b);

        return new ByteBlock(b, count);
    }

    /**
     * Reads up to len bytes of data from the input stream into an array of bytes.
     *
     * @param  b   The byte array to store the results of the read into.
     * @param  off The start offset in array b at which the data is written.
     * @param  len The maximum number of bytes to read.
     *
     * @return The same results as stored in parameter b but wrapped in byte block.
     *
     * @throws IOException     If an I/O error occurs when accessing the stream.
     * @throws RemoteException If an RMI error occurs during remote access.
     */
    public ByteBlock read(byte[] b, int off, int len) throws IOException
    {
        int count = source.read(b, off, len);

        return new ByteBlock(b, count);
    }

    /**
     * Repositions this stream to the position at the time the mark method was last called on this input stream.
     *
     * @throws IOException     If an I/O error occurs when accessing the stream.
     * @throws RemoteException If an RMI error occurs during remote access.
     */
    public void reset() throws IOException
    {
        source.reset();
    }

    /**
     * Skips over and discards n bytes of data from this input stream.
     *
     * @param  n The number of bytes to skip.
     *
     * @return The actual number of bytes skipped.
     *
     * @throws IOException     If an I/O error occurs when accessing the stream.
     * @throws RemoteException If an RMI error occurs during remote access.
     */
    public long skip(long n) throws IOException
    {
        return source.skip(n);
    }
}


/*
 * Copyright The Sett Ltd, 2005 to 2014.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.thesett.common.util.maps;

import java.util.Random;

/**
 * SequentialCuckooFunction uses cuckoo hashing, to compute a sequentially increasing function of its input. When new
 * inputs are encountered, they are assigned sequence numbers, and they are recorded in a mapping using cuckoo hashing.
 * When inputs are re-visited their existing sequence numbers are looked up very quickly (at most two probes), again
 * using cuckoo hashing.
 *
 *

CRC Card

 *
ResponsibilitiesCollaborations
 *
Compute a sequentially increasing function of arbitrary keys using cuckoo hashing.
 *

 *
 * @author Rupert Smith
 */
public class SequentialCuckooFunction<K> implements SequentialFunction<K>
{
    /** A source of random numbers to rejig the hash functions with. */
    private static final Random r = new Random();

    /** Holds the current size of the hash table. */
    private int hashTableSize = 16;

    /** Holds the maximum size of hash table that will be attempted to be allocated. */
    private final int maxSize = Integer.MAX_VALUE;

    /** Holds the count of elements currently in the hash table. */
    private int count;

    /** Holds a sequence number, for the monotonically increasing function. */
    private int nextSequenceNumber;

    /** Holds the actual hash table as an array of entries. */
    private Entry<K>[] hashTable = (Entry<K>[]) new Entry[hashTableSize];

    /** Holds the length of the hash table. */
    private int length = hashTable.length;

    /** A random seed for hash function 1. */
    private int hash1seed;

    /** A random seed for hash function 2. */
    private int hash2seed;

    /**
     * {@inheritDoc}
     *
     *
Looks up the specified key in hash table, using cuckoo hashing. If the key cannot be found in the table, then
     * the next available sequence number is allocated to it, and a new entry is added to the hash table for the key,
     * again using cuckoo hashing.
     *
     * @param  key The key to get a sequence number for.
     *
     * @return A sequence number for the key.
     *
     * @throws IllegalStateException If adding a new key to the function causes more space to be allocated than is
     *                               allowed by the maximum size limit.
     */
    public Integer apply(K key)
    {
        return applyWithEntry(key, null, true);
    }

    /** {@inheritDoc} */
    public boolean containsKey(K key)
    {
        int keyHashCode = key.hashCode();
        int hash1 = hash1(keyHashCode);

        Entry<K> entry = hashTable[indexFor(hash1)];

        if ((entry != null) && key.equals(entry.key))
        {
            return true;
        }

        entry = hashTable[indexFor(hash2(hash1, keyHashCode))];

        if ((entry != null) && key.equals(entry.key))
        {
            return true;
        }

        return false;
    }

    /** {@inheritDoc} */
    public Integer remove(Object objectKey)
    {
        K key = (K) objectKey;

        int keyHashCode = key.hashCode();
        int hash1 = hash1(keyHashCode);
        int index1 = indexFor(hash1);

        Entry<K> entry = hashTable[index1];

        if ((entry != null) && key.equals(entry.key))
        {
            hashTable[index1] = null;

            return entry.seq;
        }

        int hash2 = hash2(hash1, keyHashCode);
        int index2 = indexFor(hash2);

        entry = hashTable[index2];

        if ((entry != null) && key.equals(entry.key))
        {
            hashTable[index2] = null;

            return entry.seq;
        }

        return null;
    }

    /** {@inheritDoc} */
    public void clear()
    {
        count = 0;
        nextSequenceNumber = 0;
        hashTable = (Entry<K>[]) new Entry[hashTableSize];
        length = hashTable.length;
    }

    /**
     * Reports the number of elements held in the map.
     *
     * @return The number of elements held in the map.
     */
    public int size()
    {
        return count;
    }

    /**
     * Prints all entries in the sequential function, for debugging purposes.
     *
     * @return All entries in the sequential function.
     */
    public String toString()
    {
        String result = "SequentialCuckooFunction: [ entries = { ";

        boolean prevEntry = false;

        for (Entry<K> entry : hashTable)
        {
            if (entry != null)
            {
                result +=
                    (prevEntry ? ", " : "") + entry.seq + "->" + entry.key + "(" + entry.hash1 + ", " + entry.hash2 +
                    ")";
                prevEntry = true;
            }
        }

        return result + " } ]";
    }

    /**
     * Looks up the specified key in hash table, using cuckoo hashing. If the key cannot be found in the table, then the
     * next available sequence number is allocated to it, and a new entry is added to the hash table for the key, again
     * using cuckoo hashing.
     *
     *
To save creating entries that already exist, if an entry for the key already exists, because this method is
     * being called to rehash an old table into a new one, then the entry may be passed into this method. In that case,
     * no new entry will be created and the existing one will be re-used. It is assumed that the entry passed in is the
     * correct one for the key.
     *
     * @param  key          The key to get a sequence number for.
     * @param  entry        The entry for the key, or <tt>null</tt> to create a new one.
     * @param  tryRehashing <tt>true</tt> if rehashing should be trued, otherwise just fail.
     *
     * @return A sequence number for the key, or <tt>null</tt> if this fails.
     */
    private Integer applyWithEntry(K key, Entry<K> entry, boolean tryRehashing)
    {
        // Used to hold a new entry if one has to be created, or can re-use an entry passed in as a parameter.
        Entry<K> uninsertedEntry = entry;

        // Holds a flag to indicate that a new sequence number has been taken.
        boolean createdNewEntry = false;

        // Check if there is already an entry for the key, and return it if so.
        Entry<K> existingEntry = entryForKey(key);

        Integer result = null;

        if (existingEntry != null)
        {
            result = existingEntry.seq;
        }
        else
        {
            // Create a new entry, if one has not already been created and cached.
            if (uninsertedEntry == null)
            {
                uninsertedEntry = new Entry<K>();
                uninsertedEntry.key = key;
                uninsertedEntry.seq = nextSequenceNumber;
                nextSequenceNumber++;
                count++;
                createdNewEntry = true;
                result = uninsertedEntry.seq;
            }

            // Attempt to insert the new entry. The sequence number is only incremented when this succeeds for a new
            // entry. Existing entries that are being re-hashed into a new table will not increment the sequence
            // number.
            while (true)
            {
                // Hash the entry for the current hash functions.
                int keyHashCode = uninsertedEntry.key.hashCode();
                uninsertedEntry.hash1 = hash1(keyHashCode);
                uninsertedEntry.hash2 = hash2(uninsertedEntry.hash1, keyHashCode);

                // Try and insert the entry, checking that no entry is left uninserted as a result.
                uninsertedEntry = cuckoo(uninsertedEntry);

                if (uninsertedEntry == null)
                {
                    result = createdNewEntry ? result : -1;

                    break;
                }

                // If the cuckoo algorithm fails then change the hash function/table size and try again.
                if (tryRehashing)
                {
                    rehash();
                }
                else
                {
                    result = null;

                    break;
                }

            }
        }

        return result;
    }

    /**
     * Checks if the specified key can be found in the set, and returns its entry if so.
     *
     * @param  key The key to check for in the entry set.
     *
     * @return The keys entry if it is already in the set, <tt>null</tt> if not.
     */
    private Entry<K> entryForKey(K key)
    {
        int keyHashCode = key.hashCode();
        int hash1 = hash1(keyHashCode);

        Entry<K> entry = hashTable[indexFor(hash1)];

        if ((entry != null) && key.equals(entry.key))
        {
            return entry;
        }

        int hash2 = hash2(hash1, keyHashCode);
        entry = hashTable[indexFor(hash2)];

        if ((entry != null) && key.equals(entry.key))
        {
            return entry;
        }

        return null;
    }

    /**
     * Calculates the index offset in the table for a hash code.
     *
     * @param  h The hash code.
     *
     * @return An index for the hash code that lies within the table.
     */
    private int indexFor(int h)
    {
        return ((h < 0) ? ((length + (h % length)) % length) : (h % length));
    }

    /**
     * Adds a new entry to a hash table, using the cuckoo algorithm.
     *
     * @param  entry The new key to add.
     *
     * @return <tt>null</tt> if the cuckoo algorithm inserted the new entry, or the currently uninserted entry if it
     *         could not complete the insertion. Note that the currently uninserted entry may not be the entry
     *         originally requested to be inserted, as that may have succeeded, and a displaced entry further down the
     *         chain of cuckoo insertions may be the current uninserted one, that remains to be inserted.
     */
    private Entry<K> cuckoo(Entry<K> entry)
    {
        // Holds the entry currently being placed in the hash table.
        Entry<K> currentEntry = entry;

        // Holds the index into the hash table where the current entry will be placed.
        int hash = entry.hash1;
        int index = indexFor(hash);
        Entry<K> nextEntry = hashTable[index];

        int previousFlag = 0;
        int[] previousIndex = new int[2];
        int[] previousSeq = new int[2];

        for (int i = 0; i < hashTableSize; i++)
        {
            // Check the current index, to see if it is an empty slot. If it is an empty slot then the current
            // entry is placed there and the algorithm completes.
            if (nextEntry == null)
            {
                hashTable[index] = currentEntry;

                return null;
            }

            // If the current index does not point to an empty slot, the current entry is placed there anyway, but the
            // displaced entry (the egg displaced by the cuckoo) becomes the current entry for placing.
            hashTable[index] = currentEntry;
            currentEntry = nextEntry;

            // A new index is selected depending on whether the entry is currently at its primary or secondary hashing.
            int firstPosition = indexFor(currentEntry.hash1);
            hash = (index == firstPosition) ? currentEntry.hash2 : currentEntry.hash1;
            index = indexFor(hash);

            // A check for infinite loops of size 2 is made here, to circumvent the simplest and most common infinite
            // looping condition.
            previousIndex[previousFlag] = index;
            previousSeq[previousFlag] = nextEntry.seq;
            previousFlag = (previousFlag == 1) ? 0 : 1;

            nextEntry = hashTable[index];

            if ((nextEntry != null) && (index == previousIndex[previousFlag]) &&
                    (nextEntry.seq == previousSeq[previousFlag]))
            {
                break;
            }

        }

        return currentEntry;
    }

    /**
     * Creates a new hashtable that is twice the size of the old one, then re-hashes everything from the old table into
     * the new table.
     *
     * @throws IllegalStateException If the table cannot be increased in size because the maximum size limit would be
     *                               breached.
     */
    private void rehash()
    {
        // Increase the table size, to keep the load factory < 0.5.
        int newSize = hashTableSize;

        if (hashTableSize < (count * 2))
        {
            newSize = hashTableSize * 2;

            if (newSize > maxSize)
            {
                throw new IllegalStateException("'newSize' of " + newSize +
                    " would put the table over the maximum size limit of " + maxSize);
            }
        }

        // Keep hold of the old table, until a new one is succesfully buily.
        Entry<K>[] oldTable = hashTable;
        hashTableSize = newSize;
        length = hashTable.length;

        // Keep rehashing the table until it is succesfully rebuilt.
        boolean rehashedOk;

        do
        {
            // Start by assuming that this will work.
            rehashedOk = true;

            // Alter the hash functions.
            changeHashFunctions();

            // Create a new table from the old one, to rehash everything into.
            hashTable = (Entry<K>[]) new Entry[hashTableSize];

            for (Entry<K> entry : oldTable)
            {
                if (entry != null)
                {
                    // Add the entry to the new table, dropping out if this fails.
                    if (applyWithEntry(entry.key, entry, false) == null)
                    {
                        rehashedOk = false;

                        break;
                    }
                }
            }
        }
        while (!rehashedOk);
    }

    /** Selects new random seeds to alter the hash functions. */
    private void changeHashFunctions()
    {
        hash1seed = r.nextInt();
        hash2seed = r.nextInt();
    }

    /**
     * Implements Robert Jenkins' 32-bit integer hash function. http://www.concentric.net/~Ttwang/tech/inthash.htm
     *
     * @param  key The integer to compute a hash of.
     *
     * @return A hash of the integer.
     */
    private int hash1(int key)
    {
        key += hash1seed;
        key = (key + 0x7ed55d16) + (key << 12);
        key = (key ^ 0xc761c23c) ^ (key >> 19);
        key = (key + 0x165667b1) + (key << 5);
        key = (key + 0xd3a2646c) ^ (key << 9);
        key = (key + 0xfd7046c5) + (key << 3);
        key = (key ^ 0xb55a4f09) ^ (key >> 16);

        return key;
    }

    /**
     * Thomas Wang's 32-bit shift hash function.
     * http://www.concentric.net/~Ttwang/tech/inthash.htm
     *
     * @param  key The integer to compute a hash of.
     *
     * @return A hash of the integer.
     */
    private int hash32shift(int key)
    {
        key += hash2seed;
        key = ~key + (key << 15);
        key = key ^ (key >>> 12);
        key = key + (key << 2);
        key = key ^ (key >>> 4);
        key = key * 2057;
        key = key ^ (key >>> 16);

        return key;
    }

    /**
     * Implements a secondary hash. This uses the 32-bit shift hash implemented by {@link #hash32shift(int)} and then
     * successively applies {@link #hash1(int)} if the generated hash code is not different to the hash code generated
     * by running {@link #hash1(int)} on the key. This ensures that the hash code returned by this will be different to
     * the one generated by {@link #hash1(int)}.
     *
     * @param  hash1 The output of the hash1 function.
     * @param  key   The integer to compute a hash of.
     *
     * @return A hash of the integer which is different to that generated by {@link #hash1(int)}.
     */
    private int hash2(int hash1, int key)
    {
        key = hash32shift(key);

        while (key == hash1)
        {
            key = hash32shift(key);
        }

        return key;
    }

    /**
     * Entry is used to hold a key/value pair, along with its cached hash codes.
     *
     *

CRC Card

     *
ResponsibilitiesCollaborations
     *
Hold a key/value pair.
     *
Cache two different hashes of the key.
     *

     */
    private static class Entry<K>
    {
        /** Holds the key. */
        K key;

        /** Holds the allocated sequence number. */
        int seq;

        /** Holds the first cached hash code of the key. */
        int hash1;

        /** Holds the second cached hash code of the key. */
        int hash2;

        /**
         * Pretty prints the entry for debugging purposes.
         *
         * @return The entries key and sequence number as a string.
         */
        public String toString()
        {
            return "Entry: [ seq = " + seq + ", key = " + key.toString() + " ]";
        }
    }
}


/*
 * Copyright The Sett Ltd, 2005 to 2014.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.thesett.aima.logic.fol.wam.debugger.swing;

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Point;

import javax.swing.JViewport;
import javax.swing.ViewportLayout;

/**
 * FillViewportLayout is a modified ViewportLayout to ensure that the child components within a view-port always fill
 * the view-port when their preferred size is smaller than the view-port itself.
 *
 *
This is a JDK1.2.2 bug (id 4310721).
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Layout a component to fill a view-port.

 *

 *
 * @author Rupert Smith
 */
public class FillViewportLayout extends ViewportLayout
{
    /** {@inheritDoc} */
    public void layoutContainer(Container parent)
    {
        JViewport vp = (JViewport) parent;
        Component view = vp.getView();

        if (view == null)
        {
            return;
        }

        Point viewPosition = vp.getViewPosition();
        Dimension viewPrefSize = view.getPreferredSize();
        Dimension vpSize = vp.getSize();
        Dimension viewSize = new Dimension(viewPrefSize);

        if ((viewPosition.x == 0) && (vpSize.width > viewPrefSize.width))
        {
            viewSize.width = vpSize.width;
        }

        if ((viewPosition.y == 0) && (vpSize.height > viewPrefSize.height))
        {
            viewSize.height = vpSize.height;
        }

        if (!viewSize.equals(viewPrefSize))
        {
            vp.setViewSize(viewSize);
        }
    }
}


/*
 * Copyright The Sett Ltd, 2005 to 2014.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.thesett.common.swing.workpanel;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JPanel;

/**
 * A WorkPanel is a user interface element that is used to build screens that use a commit or cancel workflow. This is
 * usually implemented by having an ok and a cancel button and possibly also an apply button. The idea is that
 * WorkPanels will allow a user to edit data and then either drop their changes if cancel is selected or commit their
 * changes if ok or apply is selected.
 *
 *
WorkPanel interacts with three possible actions: Ok, Cancel and Apply. A WorkPanel has methods that will be called
 * to inform the panel when it should set up its data, when it is made visible and when it is made invisible.
 *
 *

CRC Card

 *
ResponsibilitiesCollaborations
 *
Handle OK, Cancel and Apply actions
 *
Initialize
 *
Save data
 *
Discard data
 *

 *
 * @author Rupert Smith
 */
public abstract class WorkPanel extends JPanel implements ActionListener
{
    /** Used for logging. */
    /* private static final Logger log = Logger.getLogger(WorkPanel.class.getName()); */

    /** Holds the state of this work panel. */
    protected WorkPanelState state;

    /** Creates a new WorkPanel object. */
    public WorkPanel()
    {
        state = new WorkPanelState();
    }

    /**
     * This method will be called when the panel is to be displayed. If the state is NOT_INTIALIZED it should load its
     * data, and build its user interface components and set the state to READY. If the state is READY or NOT_SAVED it
     * should not do anything.
     */
    public abstract void initialize();

    /**
     * This method should be implemented to save the state of the WorkPanel. It is called when an Ok or an Apply event
     * is triggered.
     */
    public abstract void saveWork();

    /**
     * This method should be implemented to discard the state of the WorkPanel. It is called when a Cancel event is
     * triggered.
     */
    public abstract void discardWork();

    /**
     * Gets the work panel state.
     *
     * @return The work panel state.
     */
    public WorkPanelState getWorkPanelState()
    {
        return state;
    }

    /**
     * Listens for the button events Ok, Cancel and Apply. If the event is Ok or Apply the saveWork method is triggered.
     * If the event is Cancel then the discardWork method is triggered.
     *
     * @param event The work panel event, ignored if it is not one of "Ok", "Cancel" or "Apply".
     */
    public void actionPerformed(ActionEvent event)
    {
        /*log.fine("void actionPerformed(ActionEvent): called");*/
        /*log.fine("Action is " + event.getActionCommand());*/

        // Check which action was performed
        String action = event.getActionCommand();

        if ("OK".equals(action))
        {
            // Check if the state is NOT_SAVED
            if (state.getState().equals(WorkPanelState.NOT_SAVED))
            {
                // Save the work
                saveWork();
            }
        }
        else if ("Cancel".equals(action))
        {
            // Check if the state is NOT_SAVED
            if (state.getState().equals(WorkPanelState.NOT_SAVED))
            {
                // Discard the work
                discardWork();
            }
        }
        else if ("Apply".equals(action))
        {
            // Check if the state is NOT_SAVED
            if (state.getState().equals(WorkPanelState.NOT_SAVED))
            {
                // Save the work
                saveWork();
            }
        }
    }

    /**
     * This method should be called once the data in the panel has been changed. It should set the state to NOT_SAVED.
     */
    public void hasChanged()
    {
        // Change the state to NOT_SAVED
        state.setState(WorkPanelState.NOT_SAVED);
    }
}


/*
 * Copyright The Sett Ltd, 2005 to 2014.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.thesett.common.swing.workflow;

import java.beans.PropertyChangeEvent;

import com.thesett.common.swing.workpanel.WorkPanelState;

/**
 * WorkFlowScreenState models the possible states of an individual screen within a work flow. A work flow is a series of
 * user inputs (often dialog boxes) that progress through stages of a process interactively. They are frequently used as
 * wizards.
 *
 *
WorkFlowScreenState inherits its fundamental state model from
 * {@link com.thesett.common.swing.workpanel.WorkPanelState} which has three states. These and NOT_INITIALIZED, the
 * state of a screen that is not yet ready to accept user input, READY, the state of a screen which is ready to accept
 * user input but is still in a clean state, and NOT_SAVED the state of a screen that has some user input but which has
 * not yet commited or done anything with that input. A NOT_SAVED screen has input but is waiting for the completion of
 * the entire work flow before it saves its state, or it may alternatively commit its state when the next or previous
 * page is requested.
 *
 *
The screen state also implements flags to determine whether or not a previous or next page is avaliable from this
 * one and whether or not its current state means that the entire work flow may be finished at this point. Screen
 * implementations may call the set methods for these flags and property change event will be triggered to notify the
 * controlling button panel of the fact that these flags have changed. The button panel will update the enabled/disabled
 * status of its buttons to reflect the current state of the flags.
 *
 *

CRC Card

 *
ResponsibilitiesCollaborations
 *
Maintain state of a screen in a work flow
 *
Provide flags for next and prev pages and for completion of work flow
 *
Notify event listeners of changes to any properties.
 *

 *
 * @author Rupert Smith
 */
public class WorkFlowScreenState extends WorkPanelState
{
    /** Used for logging. */
    /* private static final Logger log = Logger.getLogger(WorkFlowScreenState.class.getName()); */

    /** Flag determining whether another page is available in the work flow. */
    private boolean nextAvailable;

    /** Flag determining whether a pervious page is available in the work flow. */
    private boolean prevAvailable;

    /** Flag determining whether the state of the screen means that the entire flow may be ended at this point. */
    private boolean finished;

    /**
     * Checks if the next button should be enabled.
     *
     * @return The state of the next available flag.
     */
    public boolean isNextAvailable()
    {
        return nextAvailable;
    }

    /**
     * Checks if the back button should be enabled.
     *
     * @return The state of the previous available flag.
     */
    public boolean isPrevAvailable()
    {
        return prevAvailable;
    }

    /**
     * Checks if the entire work flow is complete.
     *
     * @return The work flow complete flag.
     */
    public boolean isFinished()
    {
        return finished;
    }

    /**
     * Sets the state of the next available flag and notifies any listeners of this change.
     *
     * @param avail The next page available state.
     */
    public void setNextAvailable(boolean avail)
    {
        // Check if the state has changed
        if (nextAvailable != avail)
        {
            // Keep the new state
            nextAvailable = avail;

            // Notify any listeners fo the change in state
            firePropertyChange(new PropertyChangeEvent(this, "nextAvailable", !avail, avail));
        }
    }

    /**
     * Sets the state of the previous available flag and notifies any listeners of this change.
     *
     * @param avail The previous page available state.
     */
    public void setPrevAvailable(boolean avail)
    {
        // Check if the state has changed
        if (prevAvailable != avail)
        {
            // Keep the new state
            prevAvailable = avail;

            // Notify any listeners fo the change in state
            firePropertyChange(new PropertyChangeEvent(this, "prevAvailable", !avail, avail));
        }
    }

    /**
     * Sets the state of the finished and notifies any listeners of this change.
     *
     * @param avail The work flow completed state.
     */
    public void setFinished(boolean avail)
    {
        /*log.fine("void setFinished(boolean): called");*/

        // Check if the state has changed
        if (finished != avail)
        {
            // Keep the new state
            finished = avail;

            // Notify any listeners fo the change in state
            firePropertyChange(new PropertyChangeEvent(this, "finished", !avail, avail));

            /*log.fine("fired property change event");*/
        }
    }
}


/*
 * Copyright The Sett Ltd, 2005 to 2014.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.thesett.aima.logic.fol.wam.nativemachine;

import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.Iterator;
import java.util.Set;

import com.thesett.aima.logic.fol.Variable;
import com.thesett.aima.logic.fol.wam.compiler.WAMCallPoint;
import com.thesett.aima.logic.fol.wam.machine.WAMInternalRegisters;
import com.thesett.aima.logic.fol.wam.machine.WAMMemoryLayout;
import com.thesett.aima.logic.fol.wam.machine.WAMResolvingMachine;
import com.thesett.common.error.ImplementationUnavailableException;
import com.thesett.common.error.NotImplementedException;
import com.thesett.common.util.SequenceIterator;
import com.thesett.common.util.doublemaps.SymbolTable;
import com.thesett.common.util.doublemaps.SymbolTableImpl;

/**
 * WAMResolvingNativeMachine is a byte code interpreter for WAM implemented externally as native code. The code here is
 * a wrapper for the native library. It provides a method to get an instance of this l3 machine that can throw a checked
 * exception when the native library is not available.
 *
 *
The other interesting feature of this machine, is that the byte code is passed to the native library in a direct
 * byte buffer. The intention is that the compiler (or in the future a loader) outputs compiled code into the machine
 * which holds it in a direct byte buffer. No copying accross will be done prior to execution.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Execute compiled WAM programs and queries.
 *
 Provide access to the heap.
 *

 *
 * @author Rupert Smith
 */
public class WAMResolvingNativeMachine extends WAMResolvingMachine
{
    /** Used for tracing instruction executions. */
    /* private static final Logger trace = Logger.getLogger("TRACE.WAMResolvingNativeMachine"); */

    /** Defines the initial code area size for the virtual machine. */
    private static final int CODE_SIZE = 10000;

    /** Used to record whether the native implementation library was successfully loaded. */
    private static boolean libraryFound;

    /** Used to record whether an attempt to load the native library has been made. */
    private static boolean libraryLoadAttempted;

    /**
     * Creates a unifying virtual machine for WAM with default heap sizes.
     *
     * @param symbolTable The symbol table for the machine.
     */
    public WAMResolvingNativeMachine(SymbolTable<Integer, String, Object> symbolTable)
    {
        super(symbolTable);

        // Reset the machine to its initial state.
        reset();
    }

    /**
     * Creates an instance of this machine, loading and checking for availability of the native implementation library
     * as required.
     *
     * @param  symbolTable The symbol table for the machine.
     *
     * @return An instance of the native l3 machine.
     *
     * @throws ImplementationUnavailableException If the native library cannot be loaded and linked.
     */
    public static WAMResolvingNativeMachine getInstance(SymbolTableImpl<Integer, String, Object> symbolTable)
        throws ImplementationUnavailableException
    {
        try
        {
            if (!libraryLoadAttempted)
            {
                libraryLoadAttempted = true;

                System.loadLibrary("aima_native");

                libraryFound = true;
            }

            if (libraryFound)
            {
                return new WAMResolvingNativeMachine(symbolTable);
            }
            else
            {
                throw new ImplementationUnavailableException("The native library could not be found.", null, null,
                    null);
            }
        }
        catch (UnsatisfiedLinkError e)
        {
            libraryFound = false;
            throw new ImplementationUnavailableException("The native library could not be found.", e, null, null);
        }
    }

    /**
     * Resets the machine, to its initial state. This clears any programs from the machine, and clears all of its stacks
     * and heaps.
     */
    public void reset()
    {
        // Clear the code buffer.
        codeBuffer = ByteBuffer.allocateDirect(CODE_SIZE);

        // Reset the native part of the machine.
        nativeReset();

        // Ensure that the overridden reset method of WAMBaseMachine is run too, to clear the call table.
        super.reset();
    }

    /**
     * Resets the machine, to its initial state. This clears any programs from the machine, and clears all of its stacks
     * and heaps.
     */
    public native void nativeReset();

    /**
     * Provides an iterator that generates all solutions on demand as a sequence of variable bindings.
     *
     * @return An iterator that generates all solutions on demand as a sequence of variable bindings.
     */
    public Iterator<Set<Variable>> iterator()
    {
        return new SequenceIterator<Set<Variable>>()
            {
                public Set<Variable> nextInSequence()
                {
                    return resolve();
                }
            };
    }

    /**
     * Sets the maximum number of search steps that a search method may take. If it fails to find a solution before this
     * number of steps has been reached its search method should fail and return null. What exactly constitutes a single
     * step, and the granularity of the step size, is open to different interpretation by different search algorithms.
     * The guideline is that this is the maximum number of states on which the goal test should be performed.
     *
     * @param max The maximum number of states to goal test. If this is zero or less then the maximum number of steps
     *            will not be checked for.
     */
    public void setMaxSteps(int max)
    {
        throw new NotImplementedException();
    }

    /** {@inheritDoc} */
    public IntBuffer getDataBuffer()
    {
        throw new UnsupportedOperationException();
    }

    /** {@inheritDoc} */
    public WAMInternalRegisters getInternalRegisters()
    {
        throw new UnsupportedOperationException();
    }

    /** {@inheritDoc} */
    public WAMMemoryLayout getMemoryLayout()
    {
        throw new UnsupportedOperationException();
    }

    /** {@inheritDoc} */
    protected boolean execute(WAMCallPoint callPoint)
    {
        return execute(codeBuffer, callPoint.entryPoint);
    }

    /**
     * {@inheritDoc}
     *
     *
This provides a hook in point at which the machine may, if required, compile the code down below the byte
     * code level.
     */
    protected native void codeAdded(ByteBuffer codeBuffer, int codeOffset, int length);

    /**
     * Executes a compiled byte code returning an indication of whether or not a unification was found.
     *
     * @param  codeBuffer A reference to the byte code buffer to run the machine on.
     * @param  codeOffset The start address of the compiled byte code to execute.
     *
     * @return <tt>true</tt> if a unification was found, <tt>false</tt> if the search for one failed.
     */
    protected native boolean execute(ByteBuffer codeBuffer, int codeOffset);

    /** {@inheritDoc} */
    protected native int deref(int a);

    /** {@inheritDoc} */
    protected native int derefStack(int a);

    /**
     * Gets the heap cell tag for the most recent dereference operation.
     *
     * @return The heap cell tag for the most recent dereference operation.
     */
    protected native byte getDerefTag();

    /**
     * Gets the heap call value for the most recent dereference operation.
     *
     * @return The heap call value for the most recent dereference operation.
     */
    protected native int getDerefVal();

    /**
     * Gets the value of the heap cell at the specified location.
     *
     * @param  addr The address to fetch from the heap.
     *
     * @return The heap cell at the specified location.
     */
    protected native int getHeap(int addr);

    /**
     * This is a call back onto the trace logger, that the native code can use to do any trace logging through Java,
     * instead of calling 'printf' for example.
     *
     * @param message The string to log to the tracer.
     */
    private void trace(String message)
    {
        /*trace.fine(message);*/
    }
}


/*
 * Copyright The Sett Ltd, 2005 to 2014.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.thesett.aima.math;

/**
 * InformationTheory is a math class that provides mathematical functions that relate to Shannon's information theory.
 *
 *
The {@link #expectedI}, {@link #remainder} and {@link #gain} functions all depend on knowing the probabilities of
 * the occurence of symbols. These may be derived analytically for some situations. In others they will be estimated by
 * gathering statistics. This class also provides some methods for estimating these probabilities from statistics.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Calculate expected information from a probability distribution.
 *
 Calculate remaining information to know one distribution from another.
 *
 Calaulate the gain in knowledge of one distribution from another.
 *
 Estimate a discrete probability distribution from a sample count.
 *
 Estimate a joint discrete probability distribution from a correlating sample count.
 *

 *
 * @author Rupert Smith
 */
public class InformationTheory
{
    /** Used to convert between nats and bits. This is equal to the natural logarithm of 2. */
    public static final double LN2 = Math.log(2.0d);

    /**
     * For a given set of probabilities of the occurences of symbols this function calculates the expected information
     * content of a set of symbols given its probability distribution. The answer is expressed as a positive number of
     * bits.
     *
     *
The probabilities should add up to one. This method does not provide any kind of check on this condition.
     *
     * @param  probabilities The probability distribution of the symbols. The probabilities should add to one.
     *
     * @return The expected information content, in bits, learned from being told a symbol drawn from the distribution.
     */
    public static double expectedI(double[] probabilities)
    {
        double result = 0.0d;

        // Loop over the probabilities for all the symbols calculating the contribution of each to the expected value.
        for (double p : probabilities)
        {
            // Calculate the information in each symbol. I(p) = - ln p and weight this by its probability of occurence
            // to get that symbols contribution to the expected value over all symbols.
            if (p > 0.0d)
            {
                result -= p * Math.log(p);
            }
        }

        // Convert the result from nats to bits by dividing by ln 2.
        return result / LN2;
    }

    /**
     * Supposing a stream generates pairs of symbols. Let the first be A and the second be G. A ranges over a set of
     * symbols, {h1, ... , hv} and G ranges over a set of symbols, {g1, ..., gn}.
     *
     *
G alone has a probability distribution which tells us what the expected information content of knowing what G
     * is, measured in bits.
     *
     *
This remainder function estimates how much more information is needed, if we know A, in order to know G.
     *
     *
If A and G are completely independent then this will be equal to the expected information content of G. In
     * other words A tells us nothing about G. If G is completely dependant on A then this will be equal to zero. In
     * other words as we know G already from knowing A, learning what G is will tell us nothing extra.
     *
     *
Remainder(A|G) = Sum i from 1 to v {P(hi) * expectedInformation(P(g1|hi), ..., P(gn|hi)}.
     *
     *
Where: p(g|h) = the probability of symbol g (of G) occuring given symbol h (of A).
     *
     * @param  pA       The probability distribution of the symbols of A. This will be an array of size v.
     * @param  pGgivenA The probability distribution of the symbols of G given a particular symbol of A. This will be an
     *                  array of size n by v (v arrays of size n, array is indexed as [v][n]).
     *
     * @return The expected remaining information needed to know G from knowing A.
     */
    public static double remainder(double[] pA, double[][] pGgivenA)
    {
        double result = 0.0d;

        // Loop over the probabilities for all the symbols of A calculating the contribution of each to
        // the expected value.
        for (int v = 0; v < pA.length; v++)
        {
            double phv = pA[v];

            // Calculate the expected information content of the distribution of the symbols of G given the symbol hv
            // and scale its contribution to the total by the probability of hv occuring.
            result += phv * expectedI(pGgivenA[v]);
        }

        // There is no need to convert the result from nats to bits and the expected information function is
        // already in bits.
        return result;
    }

    /**
     * Supposing a stream generates pairs of symbols. Let the first be A and the second be G. A ranges over a set of
     * symbols, {h1, ... , hv} and G ranges over a set of symbols, {g1, ..., gn}.
     *
     *
G alone has a probability distribution which tells us what the expected information content of knowing what G
     * is, measured in bits.
     *
     *
This gain function estimates how much information is gained, if we know A, about G.
     *
     *
Gain(A|G) = ExpectedI(G) - Remainder(A|G)
     *
     * @param  pG       The probability distribution of the symbols of G. This will be an array of size n.
     * @param  pA       The probability distribution of the symbols of A. This will be an array of size v.
     * @param  pGgivenA The probability distribution of the symbols of G given a particular symbol of A. This will be an
     *                  array of size n by v (v arrays of size n, array is indexed as [v][n]).
     *
     * @return the expected amount of information on G given by knowing A.
     */
    public static double gain(double[] pG, double[] pA, double[][] pGgivenA)
    {
        return expectedI(pG) - remainder(pA, pGgivenA);
    }

    /**
     * Estimates probabilities given a set of counts of occurrences of symbols.
     *
     *
P = number of times a symbol occurs/total number of symbols.
     *
     * @param  counts The counts of the occurrences of symbols over a data set of symbols.
     *
     * @return An array of the probability estimates for the occurences of the symbols. The index of the elements in
     *         this array corresponds with the index of the symbol in the input array.
     */
    public static double[] pForDistribution(int[] counts)
    {
        double[] probabilities = new double[counts.length];

        int total = 0;

        // Loop over the counts for all symbols adding up the total number.
        for (int c : counts)
        {
            total += c;
        }

        // Loop over the counts for all symbols dividing by the total number to provide a probability estimate.
        for (int i = 0; i < probabilities.length; i++)
        {
            if (total > 0)
            {
                probabilities[i] = ((double) counts[i]) / total;
            }
            else
            {
                probabilities[i] = 0.0d;
            }
        }

        return probabilities;
    }

    /**
     * Supposing a stream generates pairs of symbols. Let the first be A and the second be G. A ranges over a set of
     * symbols, {h1, ... , hv} and G ranges over a set of symbols, {g1, ..., gn}.
     *
     *
This function estimates p(g|h), the probability of a symbol g (of G) occuring given a symbol h (of A), over
     * all the symbols g and h from statistics gathered about the frequency of occurence of these symbols.
     *
     * @param  counts the counts of the occurence of the symbols of G given a particular symbol of A. This will be an
     *                array of size n by v (v arrays of size n, array is indexed as [v][n]).
     *
     * @return an estimate of the probability distribution of the symbols of G given a particular symbol of A. This will
     *         be an array of size n by v (v arrays of size n, array is indexed as [v][n]).
     */
    public static double[][] pForJointDistribution(int[][] counts)
    {
        double[][] results = new double[counts.length][];

        // Loop over all the symbols of A
        for (int i = 0; i < counts.length; i++)
        {
            // Extract the next distribution array of the symbols of G given A.
            int[] countsGgivenA = counts[i];

            // Convert the frequency distribution into a probability distribution and add it to the results.
            results[i] = pForDistribution(countsGgivenA);
        }

        return results;
    }
}


/*
 * Copyright The Sett Ltd, 2005 to 2014.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.thesett.aima.logic.fol;

/**
 * LiteralType is a base class for defining literals. Literals are not functors, atoms or variables or compound. They
 * are constant ground types.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Type as non-functor, non-atom, non-variable and non-compound.
 *
 Type as constant and ground.
 *

 *
 * @author Rupert Smith
 */
public abstract class LiteralType extends BaseTerm
{
    /**
     * Reports whether or not this term is a constant (a number of a functor of arity zero).
     *
     * @return Always <tt>true</tt>.
     */
    public boolean isConstant()
    {
        return true;
    }

    /**
     * Reports whether or not this term is a ground term.
     *
     * @return Always <tt>true</tt>.
     */
    public boolean isGround()
    {
        return true;
    }

    /** {@inheritDoc} */
    public void accept(TermVisitor visitor)
    {
        if (visitor instanceof LiteralTypeVisitor)
        {
            ((LiteralTypeVisitor) visitor).visit(this);
        }
        else
        {
            super.accept(visitor);
        }
    }
}


/*
 * Copyright The Sett Ltd, 2005 to 2014.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.thesett.aima.attribute.time;

import java.util.Calendar;

/**
 * DateOnly implements a pure date with no time of day component.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Manage a reference count against a date.
 *
 Export a date from an object pool.
 *
 Copy a date into another date.
 *

 *
 * @author Rupert Smith
 */
public class DateOnly implements DayOfYear, Comparable<DateOnly>
{
    /** Holds the year. */
    private int year;

    /** Holds the month. */
    private int month;

    /** Holds the day. */
    private int day;

    /**
     * Creates a date initialised to the day denominated by the unix timestamp given.
     *
     * @param epochOffset An offset in milliseconds since UTC midnight 01/01/1970.
     */
    public DateOnly(long epochOffset)
    {
        setTicks(epochOffset);
    }

    /**
     * Creates a data initialised to the specified value.
     *
     * @param year  The year.
     * @param month The month.
     * @param day   The day.
     */
    public DateOnly(int year, int month, int day)
    {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    /**
     * Creates a fresh date, initialised to today. This method makes use of java.util.Calendar to initialise today's
     * date.
     */
    public DateOnly()
    {
        Calendar c = Calendar.getInstance();
        year = c.get(Calendar.YEAR);
        month = c.get(Calendar.MONTH);
        day = c.get(Calendar.DATE);
    }

    /**
     * Parses a string as a date.
     *
     * @param  dateString The string to parse.
     *
     * @return The string as a DateOnly.
     */
    public static DateOnly parseDate(String dateString)
    {
        return null;
    }

    /**
     * {@inheritDoc}
     *
     *
Returns the UTC timestamp representing this date as midnight UTC on the date that this represents. If this
     * date is not a UTC date, this value will not be a UTC timestamp.
     */
    public long getTicks()
    {
        return TimeUtils.timestampToTicks(year, month, day);
    }

    /** {@inheritDoc} */
    public int getYear()
    {
        return year;
    }

    /** {@inheritDoc} */
    public int getMonth()
    {
        return month;
    }

    /** {@inheritDoc} */
    public int getDate()
    {
        return day;
    }

    /** {@inheritDoc} */
    public void setYear(int year)
    {
        this.year = year;
    }

    /** {@inheritDoc} */
    public void setMonth(int month)
    {
        this.month = month;
    }

    /** {@inheritDoc} */
    public void setDate(int date)
    {
        day = date;
    }

    /** {@inheritDoc} */
    public boolean equals(Object o)
    {
        if (this == o)
        {
            return true;
        }

        if (!(o instanceof DateOnly))
        {
            return false;
        }

        DateOnly dateOnly = (DateOnly) o;

        if (day != dateOnly.day)
        {
            return false;
        }

        if (month != dateOnly.month)
        {
            return false;
        }

        if (year != dateOnly.year)
        {
            return false;
        }

        return true;
    }

    /** {@inheritDoc} */
    public int hashCode()
    {
        int result;
        result = year;
        result = (31 * result) + month;
        result = (31 * result) + day;

        return result;
    }

    /** {@inheritDoc} */
    public int compareTo(DateOnly o)
    {
        if (year < o.year)
        {
            return -1;
        }
        else if (year > o.year)
        {
            return 1;
        }
        else if (month < o.month)
        {
            return -1;
        }
        else if (month > o.month)
        {
            return 1;
        }
        else if (day < o.day)
        {
            return -1;
        }
        else if (day > o.day)
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }

    /**
     * Pretty prints this timestamp with offset as a string, mainly for debugging purposes.
     *
     * @return This time span as a string.
     */
    public String toString()
    {
        return year + "/" + month + "/" + day;
    }

    /**
     * Sets this date from a milliseconds timestamp.
     *
     * @param ticks The timestamp.
     */
    void setTicks(long ticks)
    {
        year = TimeUtils.ticksToYears(ticks);
        month = TimeUtils.ticksToMonths(ticks);
        day = TimeUtils.ticksToDate(ticks);
    }
}


/**
 * Copyright 2010 Bazaarvoice, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @author J. Ryan Stinnett (ryan.stinnett@bazaarvoice.com)
 */

package com.bazaarvoice.jless.ast.node;

import com.bazaarvoice.jless.ast.util.NodeTreeUtils;
import com.bazaarvoice.jless.ast.visitor.InclusiveNodeVisitor;
import com.bazaarvoice.jless.ast.visitor.NodeAdditionVisitor;
import com.bazaarvoice.jless.ast.visitor.NodeNavigationVisitor;

public class SelectorNode extends InternalNode {

    public SelectorNode() {
        super();
        setAdditionVisitor();
    }

    public SelectorNode(Node child) {
        this();
        addChild(child);
    }

    private void setAdditionVisitor() {
        setAdditionVisitor(new InclusiveNodeVisitor() {
            /**
             * Absorb all children of the given selector. This assumes that cloning is not necessary.
             */
            @Override
            public boolean add(SelectorNode selector) {
                // If the first segment is a universal ("*") segment, it must remain first in the list
                SelectorSegmentNode sourceSegment = NodeTreeUtils.getFirstChild(selector, SelectorSegmentNode.class);
                if (sourceSegment.isUniversal()) {
                    // If the first destination segment is not a sub-element selector and it has no combinator,
                    // switch it to the descendant combinator before absorbing the universal segment.
                    setCombinatorIfNotSubElement(SelectorNode.this);
                    // Add the universal HTML segment at the front of the list
                    addChild(0, sourceSegment);
                }

                // If the first source segment is not a sub-element selector and it has no combinator,
                // switch it to the descendant combinator before absorbing the segments.
                setCombinatorIfNotSubElement(selector);

                NodeTreeUtils.moveChildren(selector, SelectorNode.this);
                return false; // Don't add the original selector itself
            }
        });
    }

    private void setCombinatorIfNotSubElement(SelectorNode selector) {
        SelectorSegmentNode sourceSegment = NodeTreeUtils.getFirstChild(selector, SelectorSegmentNode.class);
        if (!sourceSegment.isSubElementSelector() && sourceSegment.getCombinator().equals(SelectorSegmentNode.NO_COMBINATOR)) {
            sourceSegment.setCombinator(SelectorSegmentNode.DESCENDANT_COMBINATOR);
        }
    }

    @Override
    protected boolean add(NodeAdditionVisitor visitor) {
        return visitor.add(this);
    }

    @Override
    protected boolean enter(NodeNavigationVisitor visitor) {
        return visitor.enter(this);
    }

    @Override
    protected boolean exit(NodeNavigationVisitor visitor) {
        return visitor.exit(this);
    }

    @Override
    protected boolean visitInvisible(NodeNavigationVisitor visitor) {
        return visitor.visitInvisible(this);
    }

    /**
     * The internal state of this node will be rebuilt as cloned children are added by the super class.
     */
    @Override
    public SelectorNode clone() {
        return (SelectorNode) super.clone();
    }

    /**
     * Recreate internal state before children are cloned.
     */
    @Override
    protected void cloneChildren(InternalNode node) {
        SelectorNode selector = (SelectorNode) node;

        // Reset internal state
        selector.setAdditionVisitor();

        super.cloneChildren(node);
    }
}


/*
 * Copyright The Sett Ltd, 2005 to 2014.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.thesett.common.webapp.actions;

import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.List;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import com.thesett.common.webapp.beans.SortStateBean;
import com.thesett.common.webapp.forms.SortForm;

/**
 * SortAction applies a sorting to a list stored in a web variable scope and replaces the variable with the newly sorted
 * list. The list to be sorted is passed as a parameter as is the name of the bean which holds an implementation of the
 * interface {@link java.util.Comparator}. The sort is performed by the {@link java.util.Collections#sort} method. Once
 * the sort has been done control is returned to the "success" page which should be configured in the struts-config.xml
 * for every page that needs to do sorting to point back to that same page (unless of course you really want to navigate
 * to a new page after the sort). The idea is that the page will then be redrawn with some of its elements sorted but
 * without making any other changes to the data that the page is built from. This allows sorting to be applied to user
 * interface elements independantly of business logic and provides a re-usable piece of user interface logic that will
 * find uses in many user intefaces.
 *
 *
See {@link com.thesett.common.webapp.forms.SortForm} for information about the parameters that are passed to this
 * action.
 *
 *
If its reverse sorted, unsorted or not sorted by the current sort property then forward sort it. If its already
 * forward sorted then reverse sort it.
 *
 *

CRC Card

 *
 Responsibilities  Collaborations
 *
 Sort a list  {@link java.util.Collections#sort}
 *
 Return control to calling page.
 *

 *
 * @author Rupert Smith
 */
public class SortAction extends BaseAction
{
    /** Used for logging. */
    private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(SortAction.class.getName());

    /**
     * Processes the action.
     *
     * @param     mapping  The ActionMapping used to select this instance
     * @param     form     The optional ActionForm bean for this request (if any)
     * @param     request  The HTTP request.
     * @param     response The HTTP response.
     *
     * @return    A struts forward to the "success" location.
     *
     * @exception IOException      if an input/output error occurs
     * @exception ServletException if a servlet exception occurs
     */
    public ActionForward perform(ActionMapping mapping, ActionForm form, HttpServletRequest request,
        HttpServletResponse response) throws IOException, ServletException
    {
        log.fine("perform: called");

        // Reference the SortForm as a SortForm rather than the generic ActionForm
        SortForm sortForm = (SortForm) form;

        // Get a reference to the session scope
        HttpSession session = request.getSession();

        // Get a reference to the application scope
        ServletContext application = session.getServletContext();

        log.fine("variables in the servlet context: ");

        for (Enumeration e = application.getAttributeNames(); e.hasMoreElements();)
        {
            log.fine(e.nextElement().toString());
        }

        // Get a reference to the list to be sorted
        List list = (List) session.getAttribute(sortForm.getList());

        // Get a reference to the comparator from the application scope to use to perform the sort
        Comparator comparator = (Comparator) application.getAttribute(sortForm.getComparator());

        log.fine("comparator = " + comparator);

        // Get a reference to the current sort state (if there is one)
        SortStateBean sortStateBean = (SortStateBean) session.getAttribute(sortForm.getSortState());

        // Check if there is no sort state bean and create one if so
        if (sortStateBean == null)
        {
            log.fine("There is no sort state bean");

            sortStateBean = new SortStateBean();
        }

        // Determine whether a forward or reverse sort is to be done
        // If its reverse sorted, unsorted or not sorted by the current sort property then forward sort it
        if (!sortStateBean.getState().equals(SortStateBean.FORWARD) ||
                !sortStateBean.getSortProperty().equals(sortForm.getSortStateProperty()))
        {
            // Sort the list
            Collections.sort(list, comparator);

            // Update the current sort state
            sortStateBean.setState(SortStateBean.FORWARD);
        }

        // If its already forward sorted then reverse sort it
        else
        {
            // Sort the list
            Collections.sort(list, comparator);

            // Reverse the list
            Collections.reverse(list);

            // Update the current sort state
            sortStateBean.setState(SortStateBean.REVERSE);
        }

        // Store the sorted list in the variable from which the original list was taken
        session.setAttribute(sortForm.getList(), list);

        // Store the new sort state, setting the property that has been sorted by in the sort state
        sortStateBean.setSortProperty(sortForm.getSortStateProperty());
        session.setAttribute(sortForm.getSortState(), sortStateBean);

        // Forward to the success page
        return (mapping.findForward("success"));
    }
}


/*
 * Copyright The Sett Ltd, 2005 to 2014.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.thesett.common.throttle;

/**
 * TokenBucketThrottle is a Throttle implementation that uses a token bucket to limit the throttle rate. There is a
 * bucket of tokens (a count), which can be filled to a maximum level. Tokens are inserted into the bucket at a constant
 * rate. Every time a throttle request is made, a token is removed from the bucket. So long as there is a token
 * available to remove, the throttle request will pass.
 *
 *
The advantage of a token bucket based throttle, is that it can allow the throttle rate to over-run its defined
 * limit for a short period, until the token bucket is exhausted. In the longer term it enforces an average rate.
 *
 *
If the token bucket is empty, then the {@link Throttle#throttle()} method will block until there are some tokens
 * available. It will attempt to wait for one tokens length of time before waking up the blocked thread and adding a new
 * token. The accuracy of that wait will be constrained by the minimum wait time that the JVM can generate, which is
 * often around a few milliseconds.
 *
 *

CRC Card

 *
ResponsibilitiesCollaborations
 *
Accept throttling rate in operations per second.
 *
Inject short pauses to fill out processing cycles to a specified rate.
 *
Check against a throttle speed without waiting.
 *

 *
 * @author Rupert Smith
 */
public class TokenBucketThrottle extends BaseThrottle
{
    /** Holds the target rate for this throttle in hertz. */
    private float targetRate;

    /** Holds the maximum number of tokens in the bucket. */
    private int depth;

    /** Holds a count of the number of tokens that have been consumed. */
    private float tokenCount;

    /** Holds the time of the last throttle query. */
    private long lastTimeNanos = System.nanoTime();

    /**
     * Specifies the throttling rate in operations per second. This must be called with with a value, the inverse of
     * which is a measurement in nano seconds, such that the number of nano seconds do not overflow a long integer. The
     * value must also be larger than zero.
     *
     * @param hertz The throttling rate in cycles per second.
     */
    public void setRate(float hertz)
    {
        targetRate = hertz;
    }

    /**
     * Sets up the maximum size of the token bucket.
     *
     * @param depth The maximum size of the token bucket.
     */
    public void setDepth(int depth)
    {
        this.depth = depth;
    }

    /**
     * Checks but does not enforce the throttle rate. When this method is called, it checks if a length of time greater
     * than that equal to the inverse of the throttling rate has passed since it was last called and returned <tt>
     * true</tt>. If the length of time still to elapse to the next throttle allow point is zero or less, this method
     * will return a negative value, if there is still time to pass until the throttle allow point, this method will
     * return a positive value indicating the amount of time still to pass. A thread can wait for that period of time
     * before rechecking the throttle condition.
     *
     * @return The length of time since or still to elaps to the next throttle allow point. A negative value indicates
     *         that the throttle time has passed and a positive time indicates that there is still time to pass before
     *         it is reached. The units are in nanoseconds.
     */
    public long timeToThrottleNanos()
    {
        // Work out how long ago the last throttle query was.
        long currentTimeNanos = System.nanoTime();
        long delay = currentTimeNanos - lastTimeNanos;

        // Regenerate the tokens allowed since the last query.
        float numTokens = (delay * 1.0e9f) * targetRate;

        tokenCount -= numTokens;

        if (tokenCount < 0f)
        {
            tokenCount = 0f;
        }

        // Update the last time stamp for next time around.
        lastTimeNanos = currentTimeNanos;

        // Check if there are tokens available to consume, and consume one if so.
        if (tokenCount <= (depth - 1.0))
        {
            tokenCount += 1.0;

            return 0;
        }
        else
        {
            // There are no tokens to consume, so return an estimate of how long it will take to generate one token.
            return (long) (1.0e9 / targetRate);
        }
    }
}