text:
import { withErrorBoundary } from './SpinErrorBoundary';
import { module } from 'angular';
import { react2angular } from 'react2angular';
import { Markdown } from './Markdown';

export const CORE_PRESENTATION_MARKDOWN = 'core.presentation.markdown';
module(CORE_PRESENTATION_MARKDOWN, []).component(
  'markdown',
  react2angular(withErrorBoundary(Markdown, 'markdown'), ['message', 'tag', 'trim', 'className', 'options']),
);

redpajama_set_name: RedPajamaGithub


text:
using Nito.AsyncEx.Internal;
using Nito.AsyncEx.Internal.PlatformEnlightenment;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Nito.AsyncEx.Synchronous;

// Original idea from Stephen Toub: http://blogs.msdn.com/b/pfxteam/archive/2012/02/12/building-async-coordination-primitives-part-7-asyncreaderwriterlock.aspx

namespace Nito.AsyncEx
{
    /// <summary>
    /// A reader/writer lock that is compatible with async. Note that this lock is <b>not</b> recursive!
    /// </summary>
    [DebuggerDisplay("Id = {Id}, State = {GetStateForDebugger}, ReaderCount = {GetReaderCountForDebugger}, UpgradeInProgress = {GetUpgradeInProgressForDebugger}")]
    [DebuggerTypeProxy(typeof(DebugView))]
    public sealed class AsyncReaderWriterLock
    {
        /// <summary>
        /// The queue of TCSs that other tasks are awaiting to acquire the lock as writers.
        /// </summary>
        private readonly IAsyncWaitQueue<IDisposable> _writerQueue;

        /// <summary>
        /// The queue of TCSs that other tasks are awaiting to acquire the lock as readers.
        /// </summary>
        private readonly IAsyncWaitQueue<IDisposable> _readerQueue;

        /// <summary>
        /// The queue of TCSs that other tasks are awaiting to acquire the lock as upgradeable readers.
        /// </summary>
        private readonly IAsyncWaitQueue<UpgradeableReaderKey> _upgradeableReaderQueue;

        /// <summary>
        /// The queue of TCSs that other tasks are awaiting to upgrade a reader lock to a writer lock.
        /// </summary>
        private readonly IAsyncWaitQueue<IDisposable> _upgradeReaderQueue;

        /// <summary>
        /// The semi-unique identifier for this instance. This is 0 if the id has not yet been created.
        /// </summary>
        private int _id;

        /// <summary>
        /// The current upgradeable reader lock key, if any. If this is not <c>null</c>, then there is an upgradeable reader lock held.
        /// </summary>
        private UpgradeableReaderKey _upgradeableReaderKey;

        /// <summary>
        /// Number of reader locks held (including an upgradeable reader lock, if applicable); -1 if a writer lock is held; 0 if no locks are held.
        /// </summary>
        private int _locksHeld;

        /// <summary>
        /// The object used for mutual exclusion.
        /// </summary>
        private readonly object _mutex;

        /// <summary>
        /// A task that is completed with the reader key object for this lock.
        /// </summary>
        private readonly Task<IDisposable> _cachedReaderKeyTask;

        /// <summary>
        /// A task that is completed with the writer key object for this lock.
        /// </summary>
        private readonly Task<IDisposable> _cachedWriterKeyTask;

        [DebuggerNonUserCode]
        internal State GetStateForDebugger
        {
            get
            {
                if (_locksHeld == 0)
                    return State.Unlocked;
                if (_locksHeld == -1)
                    if (_upgradeableReaderKey != null)
                        return State.WriteLockedWithUpgradeableReader;
                    else
                        return State.WriteLocked;
                if (_upgradeableReaderKey != null)
                    return State.ReadLockedWithUpgradeableReader;
                return State.ReadLocked;
            }
        }

        internal enum State
        {
            Unlocked,
            ReadLocked,
            ReadLockedWithUpgradeableReader,
            WriteLocked,
            WriteLockedWithUpgradeableReader,
        }

        [DebuggerNonUserCode]
        internal int GetReaderCountForDebugger { get { return (_locksHeld > 0 ? _locksHeld : 0); } }
        [DebuggerNonUserCode]
        internal bool GetUpgradeInProgressForDebugger { get { return !_upgradeReaderQueue.IsEmpty; } }

        /// <summary>
        /// Creates a new async-compatible reader/writer lock.
        /// </summary>
        public AsyncReaderWriterLock(IAsyncWaitQueue<IDisposable> writerQueue, IAsyncWaitQueue<IDisposable> readerQueue,
            IAsyncWaitQueue<UpgradeableReaderKey> upgradeableReaderQueue, IAsyncWaitQueue<IDisposable> upgradeReaderQueue)
        {
            _writerQueue = writerQueue;
            _readerQueue = readerQueue;
            _upgradeableReaderQueue = upgradeableReaderQueue;
            _upgradeReaderQueue = upgradeReaderQueue;
            _mutex = new object();
            _cachedReaderKeyTask = TaskShim.FromResult<IDisposable>(new ReaderKey(this));
            _cachedWriterKeyTask = TaskShim.FromResult<IDisposable>(new WriterKey(this));
        }

        /// <summary>
        /// Creates a new async-compatible reader/writer lock.
        /// </summary>
        public AsyncReaderWriterLock()
            : this(new DefaultAsyncWaitQueue<IDisposable>(), new DefaultAsyncWaitQueue<IDisposable>(),
            new DefaultAsyncWaitQueue<UpgradeableReaderKey>(), new DefaultAsyncWaitQueue<IDisposable>())
        {
        }

        /// <summary>
        /// Gets a semi-unique identifier for this asynchronous lock.
        /// </summary>
        public int Id
        {
            get { return IdManager<AsyncReaderWriterLock>.GetId(ref _id); }
        }

        internal object SyncObject
        {
            get { return _mutex; }
        }

        /// <summary>
        /// Applies a continuation to the task that will call <see cref="ReleaseWaiters"/> if the task is canceled. This method may not be called while holding the sync lock.
        /// </summary>
        /// <param name="task">The task to observe for cancellation.</param>
        private void ReleaseWaitersWhenCanceled(Task task)
        {
            task.ContinueWith(t =>
            {
                List<IDisposable> finishes;
                lock (SyncObject) { finishes = ReleaseWaiters(); }
                foreach (var finish in finishes)
                    finish.Dispose();
            }, CancellationToken.None, TaskContinuationOptions.OnlyOnCanceled | TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
        }

        /// <summary>
        /// Asynchronously acquires the lock as a reader. Returns a disposable that releases the lock when disposed.
        /// </summary>
        /// <param name="cancellationToken">The cancellation token used to cancel the lock. If this is already set, then this method will attempt to take the lock immediately (succeeding if the lock is currently available).</param>
        /// <returns>A disposable that releases the lock when disposed.</returns>
        public AwaitableDisposable<IDisposable> ReaderLockAsync(CancellationToken cancellationToken)
        {
            Task<IDisposable> ret;
            lock (SyncObject)
            {
                // If the lock is available or in read mode and there are no waiting writers, upgradeable readers, or upgrading readers, take it immediately.
                if (_locksHeld >= 0 && _writerQueue.IsEmpty && _upgradeableReaderQueue.IsEmpty && _upgradeReaderQueue.IsEmpty)
                {
                    ++_locksHeld;
                    ret = _cachedReaderKeyTask;
                }
                else
                {
                    // Wait for the lock to become available or cancellation.
                    ret = _readerQueue.Enqueue(SyncObject, cancellationToken);
                }
                //Enlightenment.Trace.AsyncReaderWriterLock_TrackLock(this, AsyncReaderWriterLockLockType.Reader, ret);
            }

            return new AwaitableDisposable<IDisposable>(ret);
        }

        /// <summary>
        /// Synchronously acquires the lock as a reader. Returns a disposable that releases the lock when disposed. This method may block the calling thread.
        /// </summary>
        /// <param name="cancellationToken">The cancellation token used to cancel the lock. If this is already set, then this method will attempt to take the lock immediately (succeeding if the lock is currently available).</param>
        /// <returns>A disposable that releases the lock when disposed.</returns>
        public IDisposable ReaderLock(CancellationToken cancellationToken)
        {
            Task<IDisposable> ret;
            lock (SyncObject)
            {
                // If the lock is available or in read mode and there are no waiting writers, upgradeable readers, or upgrading readers, take it immediately.
                if (_locksHeld >= 0 && _writerQueue.IsEmpty && _upgradeableReaderQueue.IsEmpty && _upgradeReaderQueue.IsEmpty)
                {
                    ++_locksHeld;
                    return _cachedReaderKeyTask.Result;
                }

                // Wait for the lock to become available or cancellation.
                ret = _readerQueue.Enqueue(SyncObject, cancellationToken);
            }

            return ret.WaitAndUnwrapException();
        }

        /// <summary>
        /// Asynchronously acquires the lock as a reader. Returns a disposable that releases the lock when disposed.
        /// </summary>
        /// <returns>A disposable that releases the lock when disposed.</returns>
        public AwaitableDisposable<IDisposable> ReaderLockAsync()
        {
            return ReaderLockAsync(CancellationToken.None);
        }

        /// <summary>
        /// Synchronously acquires the lock as a reader. Returns a disposable that releases the lock when disposed. This method may block the calling thread.
        /// </summary>
        /// <returns>A disposable that releases the lock when disposed.</returns>
        public IDisposable ReaderLock()
        {
            return ReaderLock(CancellationToken.None);
        }

        /// <summary>
        /// Asynchronously acquires the lock as a writer. Returns a disposable that releases the lock when disposed.
        /// </summary>
        /// <param name="cancellationToken">The cancellation token used to cancel the lock. If this is already set, then this method will attempt to take the lock immediately (succeeding if the lock is currently available).</param>
        /// <returns>A disposable that releases the lock when disposed.</returns>
        public AwaitableDisposable<IDisposable> WriterLockAsync(CancellationToken cancellationToken)
        {
            Task<IDisposable> ret;
            lock (SyncObject)
            {
                // If the lock is available, take it immediately.
                if (_locksHeld == 0)
                {
                    _locksHeld = -1;
                    ret = _cachedWriterKeyTask;
                }
                else
                {
                    // Wait for the lock to become available or cancellation.
                    ret = _writerQueue.Enqueue(SyncObject, cancellationToken);
                }
                //Enlightenment.Trace.AsyncReaderWriterLock_TrackLock(this, AsyncReaderWriterLockLockType.Writer, ret);
            }

            ReleaseWaitersWhenCanceled(ret);
            return new AwaitableDisposable<IDisposable>(ret);
        }

        /// <summary>
        /// Synchronously acquires the lock as a writer. Returns a disposable that releases the lock when disposed. This method may block the calling thread.
        /// </summary>
        /// <param name="cancellationToken">The cancellation token used to cancel the lock. If this is already set, then this method will attempt to take the lock immediately (succeeding if the lock is currently available).</param>
        /// <returns>A disposable that releases the lock when disposed.</returns>
        public IDisposable WriterLock(CancellationToken cancellationToken)
        {
            Task<IDisposable> ret;
            lock (SyncObject)
            {
                // If the lock is available, take it immediately.
                if (_locksHeld == 0)
                {
                    _locksHeld = -1;
                    return _cachedWriterKeyTask.Result;
                }

                // Wait for the lock to become available or cancellation.
                ret = _writerQueue.Enqueue(SyncObject, cancellationToken);
            }

            ReleaseWaitersWhenCanceled(ret);
            return ret.WaitAndUnwrapException();
        }

        /// <summary>
        /// Asynchronously acquires the lock as a writer. Returns a disposable that releases the lock when disposed.
        /// </summary>
        /// <returns>A disposable that releases the lock when disposed.</returns>
        public AwaitableDisposable<IDisposable> WriterLockAsync()
        {
            return WriterLockAsync(CancellationToken.None);
        }

        /// <summary>
        /// Asynchronously acquires the lock as a writer. Returns a disposable that releases the lock when disposed. This method may block the calling thread.
        /// </summary>
        /// <returns>A disposable that releases the lock when disposed.</returns>
        public IDisposable WriterLock()
        {
            return WriterLock(CancellationToken.None);
        }

        /// <summary>
        /// Asynchronously acquires the lock as a reader with the option to upgrade. Returns a key that can be used to upgrade and downgrade the lock, and releases the lock when disposed.
        /// </summary>
        /// <param name="cancellationToken">The cancellation token used to cancel the lock. If this is already set, then this method will attempt to take the lock immediately (succeeding if the lock is currently available).</param>
        /// <returns>A key that can be used to upgrade and downgrade this lock, and releases the lock when disposed.</returns>
        public AwaitableDisposable<UpgradeableReaderKey> UpgradeableReaderLockAsync(CancellationToken cancellationToken)
        {
            Task<UpgradeableReaderKey> ret;
            lock (SyncObject)
            {
                // If the lock is available, take it immediately.
                if (_locksHeld == 0 || (_locksHeld > 0 && _upgradeableReaderKey == null))
                {
                    ++_locksHeld;
                    _upgradeableReaderKey = new UpgradeableReaderKey(this);
                    ret = TaskShim.FromResult(_upgradeableReaderKey);
                }
                else
                {
                    // Wait for the lock to become available or cancellation.
                    ret = _upgradeableReaderQueue.Enqueue(SyncObject, cancellationToken);
                }
                //Enlightenment.Trace.AsyncReaderWriterLock_TrackLock(this, AsyncReaderWriterLockLockType.UpgradeableReader, ret);
            }

            ReleaseWaitersWhenCanceled(ret);
            return new AwaitableDisposable<UpgradeableReaderKey>(ret);
        }

        /// <summary>
        /// Synchronously acquires the lock as a reader with the option to upgrade. Returns a key that can be used to upgrade and downgrade the lock, and releases the lock when disposed. This method may block the calling thread.
        /// </summary>
        /// <param name="cancellationToken">The cancellation token used to cancel the lock. If this is already set, then this method will attempt to take the lock immediately (succeeding if the lock is currently available).</param>
        /// <returns>A key that can be used to upgrade and downgrade this lock, and releases the lock when disposed.</returns>
        public UpgradeableReaderKey UpgradeableReaderLock(CancellationToken cancellationToken)
        {
            Task<UpgradeableReaderKey> ret;
            lock (SyncObject)
            {
                // If the lock is available, take it immediately.
                if (_locksHeld == 0 || (_locksHeld > 0 && _upgradeableReaderKey == null))
                {
                    ++_locksHeld;
                    _upgradeableReaderKey = new UpgradeableReaderKey(this);
                    return _upgradeableReaderKey;
                }

                // Wait for the lock to become available or cancellation.
                ret = _upgradeableReaderQueue.Enqueue(SyncObject, cancellationToken);
            }

            ReleaseWaitersWhenCanceled(ret);
            return ret.WaitAndUnwrapException();
        }

        /// <summary>
        /// Asynchronously acquires the lock as a reader with the option to upgrade. Returns a key that can be used to upgrade and downgrade the lock, and releases the lock when disposed.
        /// </summary>
        /// <returns>A key that can be used to upgrade and downgrade this lock, and releases the lock when disposed.</returns>
        public AwaitableDisposable<UpgradeableReaderKey> UpgradeableReaderLockAsync()
        {
            return UpgradeableReaderLockAsync(CancellationToken.None);
        }

        /// <summary>
        /// Synchronously acquires the lock as a reader with the option to upgrade. Returns a key that can be used to upgrade and downgrade the lock, and releases the lock when disposed. This method may block the calling thread.
        /// </summary>
        /// <returns>A key that can be used to upgrade and downgrade this lock, and releases the lock when disposed.</returns>
        public UpgradeableReaderKey UpgradeableReaderLock()
        {
            return UpgradeableReaderLock(CancellationToken.None);
        }

        /// <summary>
        /// Asynchronously upgrades a reader lock to a writer lock. This method assumes the sync lock is already held.
        /// </summary>
        internal Task<IDisposable> UpgradeAsync(CancellationToken cancellationToken)
        {
            Task<IDisposable> ret;

            // If the lock is available, take it immediately.
            if (_locksHeld == 1)
            {
                _locksHeld = -1;
                ret = _upgradeableReaderKey._cachedUpgradeKeyTask;
            }
            else
            {
                // Wait for the lock to become available or cancellation.
                ret = _upgradeReaderQueue.Enqueue(SyncObject, cancellationToken);
            }

            //Enlightenment.Trace.AsyncReaderWriterLock_TrackLock(this, AsyncReaderWriterLockLockType.UpgradingToWriter, ret);
            return ret;
        }

        /// <summary>
        /// Downgrades a writer lock to a reader lock. This method assumes the sync lock is already held.
        /// </summary>
        internal List<IDisposable> Downgrade()
        {
            _locksHeld = 1;
            //Enlightenment.Trace.AsyncReaderWriterLock_LockReleased(this, AsyncReaderWriterLockLockType.UpgradingToWriter);
            return ReleaseWaiters();
        }

        /// <summary>
        /// Grants lock(s) to waiting tasks. This method assumes the sync lock is already held.
        /// </summary>
        private List<IDisposable> ReleaseWaiters()
        {
            var ret = new List<IDisposable>();

            if (_locksHeld == 0)
            {
                // Give priority to writers.
                if (!_writerQueue.IsEmpty)
                {
                    ret.Add(_writerQueue.Dequeue(_cachedWriterKeyTask.Result));
                    _locksHeld = -1;
                    return ret;
                }

                // Then to upgradeable readers.
                if (!_upgradeableReaderQueue.IsEmpty)
                {
                    _upgradeableReaderKey = new UpgradeableReaderKey(this);
                    ret.Add(_upgradeableReaderQueue.Dequeue(_upgradeableReaderKey));
                    ++_locksHeld;
                }

                // Finally to readers.
                while (!_readerQueue.IsEmpty)
                {
                    ret.Add(_readerQueue.Dequeue(_cachedReaderKeyTask.Result));
                    ++_locksHeld;
                }

                return ret;
            }

            // Give priority to upgrading readers.
            if (_locksHeld == 1)
            {
                if (!_upgradeReaderQueue.IsEmpty)
                {
                    ret.Add(_upgradeReaderQueue.Dequeue(_upgradeableReaderKey._cachedUpgradeKeyTask.Result));
                    _locksHeld = -1;
                }
            }

            if (_locksHeld > 0)
            {
                // If there are current reader locks and waiting writers, then do nothing.
                if (!_writerQueue.IsEmpty || !_upgradeableReaderQueue.IsEmpty || !_upgradeReaderQueue.IsEmpty)
                    return ret;

                // If there are current reader locks but no upgradeable reader lock, try to release an upgradeable reader.
                if (_upgradeableReaderKey == null && !_upgradeableReaderQueue.IsEmpty)
                {
                    _upgradeableReaderKey = new UpgradeableReaderKey(this);
                    ret.Add(_upgradeableReaderQueue.Dequeue(_upgradeableReaderKey));
                }
            }

            return ret;
        }

        /// <summary>
        /// Releases the lock as a reader.
        /// </summary>
        internal void ReleaseReaderLock()
        {
            List<IDisposable> finishes;
            lock (SyncObject)
            {
                --_locksHeld;
                //Enlightenment.Trace.AsyncReaderWriterLock_LockReleased(this, AsyncReaderWriterLockLockType.Reader);
                finishes = ReleaseWaiters();
            }
            foreach (var finish in finishes)
                finish.Dispose();
        }

        /// <summary>
        /// Releases the lock as a writer.
        /// </summary>
        internal void ReleaseWriterLock()
        {
            List<IDisposable> finishes;
            lock (SyncObject)
            {
                _locksHeld = 0;
                //Enlightenment.Trace.AsyncReaderWriterLock_LockReleased(this, AsyncReaderWriterLockLockType.Writer);
                finishes = ReleaseWaiters();
            }
            foreach (var finish in finishes)
                finish.Dispose();
        }

        /// <summary>
        /// Releases the lock as an upgradeable reader.
        /// </summary>
        internal void ReleaseUpgradeableReaderLock(Task upgrade)
        {
            IDisposable cancelFinish = null;
            List<IDisposable> finishes;
            lock (SyncObject)
            {
                if (upgrade != null)
                    cancelFinish = _upgradeReaderQueue.TryCancel(upgrade);
                _upgradeableReaderKey = null;
                //Enlightenment.Trace.AsyncReaderWriterLock_LockReleased(this, AsyncReaderWriterLockLockType.UpgradeableReader);
                --_locksHeld;
                finishes = ReleaseWaiters();
            }
            if (cancelFinish != null)
                cancelFinish.Dispose();
            foreach (var finish in finishes)
                finish.Dispose();
        }

        /// <summary>
        /// The disposable which releases the reader lock.
        /// </summary>
        private sealed class ReaderKey : IDisposable
        {
            /// <summary>
            /// The lock to release.
            /// </summary>
            private readonly AsyncReaderWriterLock _asyncReaderWriterLock;

            /// <summary>
            /// Creates the key for a lock.
            /// </summary>
            /// <param name="asyncReaderWriterLock">The lock to release. May not be <c>null</c>.</param>
            public ReaderKey(AsyncReaderWriterLock asyncReaderWriterLock)
            {
                _asyncReaderWriterLock = asyncReaderWriterLock;
            }

            /// <summary>
            /// Release the lock.
            /// </summary>
            public void Dispose()
            {
                _asyncReaderWriterLock.ReleaseReaderLock();
            }
        }

        /// <summary>
        /// The disposable which releases the writer lock.
        /// </summary>
        private sealed class WriterKey : IDisposable
        {
            /// <summary>
            /// The lock to release.
            /// </summary>
            private readonly AsyncReaderWriterLock _asyncReaderWriterLock;

            /// <summary>
            /// Creates the key for a lock.
            /// </summary>
            /// <param name="asyncReaderWriterLock">The lock to release. May not be <c>null</c>.</param>
            public WriterKey(AsyncReaderWriterLock asyncReaderWriterLock)
            {
                _asyncReaderWriterLock = asyncReaderWriterLock;
            }

            /// <summary>
            /// Release the lock.
            /// </summary>
            public void Dispose()
            {
                _asyncReaderWriterLock.ReleaseWriterLock();
            }
        }

        /// <summary>
        /// The disposable which manages the upgradeable reader lock.
        /// </summary>
        [DebuggerDisplay("State = {GetStateForDebugger}, ReaderWriterLockId = {_asyncReaderWriterLock.Id}")]
        public sealed class UpgradeableReaderKey : IDisposable
        {
            /// <summary>
            /// The lock to release.
            /// </summary>
            private readonly AsyncReaderWriterLock _asyncReaderWriterLock;

            /// <summary>
            /// The task doing the upgrade.
            /// </summary>
            private Task<IDisposable> _upgrade;

            /// <summary>
            /// A task that is completed with the upgrade key object for this key.
            /// </summary>
            internal readonly Task<IDisposable> _cachedUpgradeKeyTask;

            [DebuggerNonUserCode]
            internal State GetStateForDebugger
            {
                get
                {
                    if (_upgrade == null)
                        return State.Reader;
                    if (_upgrade.Status == TaskStatus.RanToCompletion)
                        return State.Writer;
                    return State.UpgradingToWriter;
                }
            }

            internal enum State
            {
                Reader,
                UpgradingToWriter,
                Writer,
            }

            /// <summary>
            /// Creates the key for a lock.
            /// </summary>
            /// <param name="asyncReaderWriterLock">The lock to release. May not be <c>null</c>.</param>
            internal UpgradeableReaderKey(AsyncReaderWriterLock asyncReaderWriterLock)
            {
                _asyncReaderWriterLock = asyncReaderWriterLock;
                _cachedUpgradeKeyTask = TaskShim.FromResult<IDisposable>(new UpgradeKey(this));
            }

            /// <summary>
            /// Gets a value indicating whether this lock has been upgraded to a write lock.
            /// </summary>
            public bool Upgraded
            {
                get
                {
                    Task task;
                    lock (_asyncReaderWriterLock.SyncObject) { task = _upgrade; }
                    return (task != null && task.Status == TaskStatus.RanToCompletion);
                }
            }

            /// <summary>
            /// Upgrades the reader lock to a writer lock. Returns a disposable that downgrades the writer lock to a reader lock when disposed.
            /// </summary>
            /// <param name="cancellationToken">The cancellation token used to cancel the upgrade. If this is already set, then this method will attempt to upgrade immediately (succeeding if the lock is currently available).</param>
            public AwaitableDisposable<IDisposable> UpgradeAsync(CancellationToken cancellationToken)
            {
                lock (_asyncReaderWriterLock.SyncObject)
                {
                    if (_upgrade != null)
                        throw new InvalidOperationException("Cannot upgrade.");

                    _upgrade = _asyncReaderWriterLock.UpgradeAsync(cancellationToken);
                }

                _asyncReaderWriterLock.ReleaseWaitersWhenCanceled(_upgrade);
                var ret = new TaskCompletionSource<IDisposable>();
                _upgrade.ContinueWith(t =>
                {
                    if (t.IsCanceled)
                        lock (_asyncReaderWriterLock.SyncObject) { _upgrade = null; }
                    ret.TryCompleteFromCompletedTask(t);
                }, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
                return new AwaitableDisposable<IDisposable>(ret.Task);
            }

            /// <summary>
            /// Synchronously upgrades the reader lock to a writer lock. Returns a disposable that downgrades the writer lock to a reader lock when disposed. This method may block the calling thread.
            /// </summary>
            /// <param name="cancellationToken">The cancellation token used to cancel the upgrade. If this is already set, then this method will attempt to upgrade immediately (succeeding if the lock is currently available).</param>
            public IDisposable Upgrade(CancellationToken cancellationToken)
            {
                lock (_asyncReaderWriterLock.SyncObject)
                {
                    if (_upgrade != null)
                        throw new InvalidOperationException("Cannot upgrade.");

                    _upgrade = _asyncReaderWriterLock.UpgradeAsync(cancellationToken);
                }

                _asyncReaderWriterLock.ReleaseWaitersWhenCanceled(_upgrade);
                var ret = new TaskCompletionSource<IDisposable>();
                _upgrade.ContinueWith(t =>
                {
                    if (t.IsCanceled)
                        lock (_asyncReaderWriterLock.SyncObject) { _upgrade = null; }
                    ret.TryCompleteFromCompletedTask(t);
                }, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
                return ret.Task.WaitAndUnwrapException();
            }

            /// <summary>
            /// Upgrades the reader lock to a writer lock. Returns a disposable that downgrades the writer lock to a reader lock when disposed.
            /// </summary>
            public AwaitableDisposable<IDisposable> UpgradeAsync()
            {
                return UpgradeAsync(CancellationToken.None);
            }

            /// <summary>
            /// Synchronously upgrades the reader lock to a writer lock. Returns a disposable that downgrades the writer lock to a reader lock when disposed. This method may block the calling thread.
            /// </summary>
            public IDisposable Upgrade()
            {
                return Upgrade(CancellationToken.None);
            }

            /// <summary>
            /// Downgrades the writer lock to a reader lock.
            /// </summary>
            private void Downgrade()
            {
                List<IDisposable> finishes;
                lock (_asyncReaderWriterLock.SyncObject)
                {
                    finishes = _asyncReaderWriterLock.Downgrade();
                    _upgrade = null;
                }
                foreach (var finish in finishes)
                    finish.Dispose();
            }

            /// <summary>
            /// Release the lock.
            /// </summary>
            public void Dispose()
            {
                _asyncReaderWriterLock.ReleaseUpgradeableReaderLock(_upgrade);
            }

            /// <summary>
            /// The disposable which downgrades an upgradeable reader key.
            /// </summary>
            internal sealed class UpgradeKey : IDisposable
            {
                /// <summary>
                /// The upgradeable reader key to downgrade.
                /// </summary>
                private readonly UpgradeableReaderKey _key;

                /// <summary>
                /// Creates the upgrade key for an upgradeable reader key.
                /// </summary>
                /// <param name="key">The upgradeable reader key to downgrade. May not be <c>null</c>.</param>
                public UpgradeKey(UpgradeableReaderKey key)
                {
                    _key = key;
                }

                /// <summary>
                /// Downgrade the upgradeable reader key.
                /// </summary>
                public void Dispose()
                {
                    _key.Downgrade();
                }
            }
        }

        // ReSharper disable UnusedMember.Local
        [DebuggerNonUserCode]
        private sealed class DebugView
        {
            private readonly AsyncReaderWriterLock _rwl;

            public DebugView(AsyncReaderWriterLock rwl)
            {
                _rwl = rwl;
            }

            public int Id { get { return _rwl.Id; } }

            public State State { get { return _rwl.GetStateForDebugger; } }

            public int ReaderCount { get { return _rwl.GetReaderCountForDebugger; } }

            public bool UpgradeInProgress { get { return _rwl.GetUpgradeInProgressForDebugger; } }

            public IAsyncWaitQueue<IDisposable> ReaderWaitQueue { get { return _rwl._readerQueue; } }

            public IAsyncWaitQueue<IDisposable> WriterWaitQueue { get { return _rwl._writerQueue; } }

            public IAsyncWaitQueue<UpgradeableReaderKey> UpgradeableReaderWaitQueue { get { return _rwl._upgradeableReaderQueue; } }

            public IAsyncWaitQueue<IDisposable> UpgradeReaderWaitQueue { get { return _rwl._upgradeReaderQueue; } }
        }
        // ReSharper restore UnusedMember.Local
    }
}


text:
export class DeviceAppDataFactory implements Mobile.IDeviceAppDataFactory {
	constructor(private $deviceAppDataProvider: Mobile.IDeviceAppDataProvider,
		private $injector: IInjector,
		private $options: ICommonOptions) { }

	create<T>(appIdentifier: string, platform: string, device: Mobile.IDevice, liveSyncOptions?: { isForCompanionApp: boolean }): T {
		const factoryRules = this.$deviceAppDataProvider.createFactoryRules();
		const isForCompanionApp = (liveSyncOptions && liveSyncOptions.isForCompanionApp) || this.$options.companion;
		const ctor = (<any>factoryRules[platform])[isForCompanionApp ? "companion" : "vanilla"];
		return this.$injector.resolve(ctor, { _appIdentifier: appIdentifier, device: device, platform: platform });
	}
}
$injector.register("deviceAppDataFactory", DeviceAppDataFactory);


text:
<!doctype html>

  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <meta name="viewport" content="width=device-width,initial-scale=1">

  <title>311 FM</title>

  <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.4.4/leaflet.css" />

  <link rel="shortcut icon" href="/assets/img/favicon.ico">
  <link rel="stylesheet" href="/assets/css/index.css">


text:
package org.unitime.timetable.server.solver;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.Vector;

import org.cpsolver.coursett.model.TimeLocation;
import org.cpsolver.coursett.preference.PreferenceCombination;
import org.hibernate.Query;
import org.hibernate.type.LongType;
import org.unitime.commons.Debug;
import org.unitime.timetable.defaults.ApplicationProperty;
import org.unitime.timetable.events.EventLookupBackend;
import org.unitime.timetable.gwt.server.DayCode;
import org.unitime.timetable.gwt.shared.TimetableGridInterface.TimetableGridBackground;
import org.unitime.timetable.gwt.shared.TimetableGridInterface.TimetableGridCell;
import org.unitime.timetable.gwt.shared.TimetableGridInterface.TimetableGridCell.Property;
import org.unitime.timetable.gwt.shared.TimetableGridInterface.TimetableGridModel;
import org.unitime.timetable.interfaces.RoomAvailabilityInterface;
import org.unitime.timetable.interfaces.RoomAvailabilityInterface.TimeBlock;
import org.unitime.timetable.model.Assignment;
import org.unitime.timetable.model.ClassInstructor;
import org.unitime.timetable.model.CourseOffering;
import org.unitime.timetable.model.CurriculumClassification;
import org.unitime.timetable.model.DatePattern;
import org.unitime.timetable.model.Department;
import org.unitime.timetable.model.DepartmentalInstructor;
import org.unitime.timetable.model.ExactTimeMins;
import org.unitime.timetable.model.Location;
import org.unitime.timetable.model.PreferenceLevel;
import org.unitime.timetable.model.Room;
import org.unitime.timetable.model.RoomSharingModel;
import org.unitime.timetable.model.Session;
import org.unitime.timetable.model.Solution;
import org.unitime.timetable.model.StudentGroup;
import org.unitime.timetable.model.SubjectArea;
import org.unitime.timetable.model.TimePattern;
import org.unitime.timetable.model.dao.SolutionDAO;
import org.unitime.timetable.solver.ui.AssignmentPreferenceInfo;
import org.unitime.timetable.solver.ui.GroupConstraintInfo;
import org.unitime.timetable.solver.ui.StudentGroupInfo;
import org.unitime.timetable.util.Constants;
import org.unitime.timetable.util.DateUtils;
import org.unitime.timetable.util.Formats;
import org.unitime.timetable.util.NameFormat;
import org.unitime.timetable.util.RoomAvailability;
import org.unitime.timetable.util.duration.DurationModel;

/**
 * @author Tomas Muller
 */
public class TimetableGridSolutionHelper extends TimetableGridHelper {
	private static DecimalFormat sDF = new DecimalFormat("0.0");

	protected static void createCells(TimetableGridModel model, List<Assignment> assignments, org.hibernate.Session hibSession, TimetableGridContext context, boolean notAvailable) {
    	for (Assignment assignment: assignments) {
    		createCells(model, assignment, hibSession, context, false);
		}
    }

	protected static List<TimetableGridCell> createCells(TimetableGridModel model, Assignment assignment, org.hibernate.Session hibSession, TimetableGridContext context, boolean notAvailable) {
    	List<TimetableGridCell> cells = new ArrayList<TimetableGridCell>();
    	TimetableGridCell cell = null;

		int days = assignment.getDays().intValue();
		int start = assignment.getStartSlot().intValue();

        BitSet weekCode = (assignment.getDatePattern() == null ? null : assignment.getDatePattern().getPatternBitSet());

		for (int j = 0; j < Constants.DAY_CODES.length; j++) {
			int d = (j + context.getWeekOffset()) % 7;
			if ((Constants.DAY_CODES[d] & days) == 0) continue;
			if (context.getFirstDay() >= 0) {
				int day = context.getFirstDay() + j;
				if (!weekCode.get(day)) continue;
			}
			if (cell == null)
				cell = createCell(model, d, start, hibSession, assignment, context, notAvailable);
			else {
				cell = new TimetableGridCell(cell, d, formatDatePattern(assignment.getDatePattern(), Constants.DAY_CODES[d]));
			}

			model.addCell(cell);
			cells.add(cell);
		}

		return cells;
    }

	protected static void createMeetingCells(TimetableGridModel model, Session session, TimetableGridContext context, Collection<TimeBlock> times, String room) {
		if (times == null) return;
		int sessionYear = session.getSessionStartYear();
        int firstDOY = session.getDayOfYear(1, session.getPatternStartMonth());
        int lastDOY = session.getDayOfYear(0, session.getPatternEndMonth() + 1);
        Calendar c = Calendar.getInstance(Locale.US);
        Formats.Format<Date> df = Formats.getDateFormat(Formats.Pattern.DATE_EVENT_SHORT);
		for (TimeBlock time: times) {
			if (time.getEndTime().before(context.getSessionStartDate()) || time.getStartTime().after(context.getSessionEndDate())) continue;
            int dayCode = 0;
            c.setTime(time.getStartTime());
            int m = c.get(Calendar.MONTH);
            int d = c.get(Calendar.DAY_OF_MONTH);
            if (c.get(Calendar.YEAR)<sessionYear) m-=(12 * (sessionYear - c.get(Calendar.YEAR)));
            if (c.get(Calendar.YEAR)>sessionYear) m+=(12 * (c.get(Calendar.YEAR) - sessionYear));
            BitSet weekCode = new BitSet(lastDOY - firstDOY);
            int offset = session.getDayOfYear(d,m) - firstDOY;
            weekCode.set(offset);
            switch (c.get(Calendar.DAY_OF_WEEK)) {
                case Calendar.MONDAY    : dayCode = Constants.DAY_CODES[Constants.DAY_MON]; break;
                case Calendar.TUESDAY   : dayCode = Constants.DAY_CODES[Constants.DAY_TUE]; break;
                case Calendar.WEDNESDAY : dayCode = Constants.DAY_CODES[Constants.DAY_WED]; break;
                case Calendar.THURSDAY  : dayCode = Constants.DAY_CODES[Constants.DAY_THU]; break;
                case Calendar.FRIDAY    : dayCode = Constants.DAY_CODES[Constants.DAY_FRI]; break;
                case Calendar.SATURDAY  : dayCode = Constants.DAY_CODES[Constants.DAY_SAT]; break;
                case Calendar.SUNDAY    : dayCode = Constants.DAY_CODES[Constants.DAY_SUN]; break;
            }
            int startSlot = (c.get(Calendar.HOUR_OF_DAY)*60 + c.get(Calendar.MINUTE) - Constants.FIRST_SLOT_TIME_MIN) / Constants.SLOT_LENGTH_MIN;
            c.setTime(time.getEndTime());
            int endSlot = (c.get(Calendar.HOUR_OF_DAY)*60 + c.get(Calendar.MINUTE) - Constants.FIRST_SLOT_TIME_MIN) / Constants.SLOT_LENGTH_MIN;
            int length = endSlot - startSlot;
            if (length<=0) continue;
            TimeLocation timeLocation = new TimeLocation(dayCode, startSlot, length, 0, 0, null, df.format(time.getStartTime()), weekCode, 0);
    		TimetableGridCell cell = null;
    		for (Enumeration<Integer> f=timeLocation.getStartSlots();f.hasMoreElements();) {
    			int slot = f.nextElement();
    			int idx = (7 + slot/Constants.SLOTS_PER_DAY - context.getWeekOffset()) % 7;
    			if (context.getFirstDay()>=0 && !timeLocation.getWeekCode().get(context.getFirstDay() + idx)) continue;
    			if (cell == null) {
    				cell = new TimetableGridCell();
    				cell.setType(TimetableGridCell.Type.Event);
    				cell.setId(time.getEventId());
    				cell.setDay(slot / Constants.SLOTS_PER_DAY);
    				cell.setSlot(slot % Constants.SLOTS_PER_DAY);
    				cell.addRoom(room);
    				cell.addName(time.getEventName());
    				cell.setProperty(Property.EventType, time.getEventType());
    				cell.setBackground(sBgColorNotAvailable);
    				cell.setLength(length);
    				cell.setTime(Constants.toTime(Constants.SLOT_LENGTH_MIN * startSlot + Constants.FIRST_SLOT_TIME_MIN) + " - " + Constants.toTime(Constants.SLOT_LENGTH_MIN  * endSlot + Constants.FIRST_SLOT_TIME_MIN));
    				cell.setDate(df.format(time.getStartTime()));
    				cell.setWeekCode(pattern2string(weekCode));
    			} else {
    				cell = new TimetableGridCell(cell, slot / Constants.SLOTS_PER_DAY, null);
    			}
    			model.addCell(cell);
    		}
		}
    }

    protected static TimetableGridCell createCell(TimetableGridModel model, int day, int slot, org.hibernate.Session hibSession, Assignment assignment, TimetableGridContext context, boolean notAvailable) {
    	TimetableGridCell cell = new TimetableGridCell();
    	cell.setType(TimetableGridCell.Type.Class);
    	cell.setId(assignment.getClassId());
    	CourseOffering course = assignment.getClazz().getSchedulingSubpart().getControllingCourseOffering();
    	if (context.isShowClassNameTwoLines()) {
    		cell.addName(assignment.getClazz().getCourseName());
    		String label = assignment.getClazz().getItypeDesc().trim() + " " + assignment.getClazz().getSectionNumberString();
    		if (context.isShowClassSuffix()) {
    			String extId = assignment.getClazz().getClassSuffix(course);
    			if (extId != null && !extId.isEmpty() && !extId.equalsIgnoreCase(assignment.getClazz().getSectionNumberString()))
    				label += " - " + extId;
    		}
    		if (context.isShowConfigName() && assignment.getClazz().getSchedulingSubpart().getInstrOfferingConfig().getInstructionalOffering().getInstrOfferingConfigs().size() > 1) {
    			label += " (" + assignment.getClazz().getSchedulingSubpart().getInstrOfferingConfig().getName() + ")";
    		}
    		cell.addName(label);
    	} else {
    		cell.addName(assignment.getClazz().getClassLabel(context.isShowClassSuffix(), context.isShowConfigName()));
    	}
    	if (context.isShowCourseTitle() && course.getTitle() != null && !course.getTitle().isEmpty()) {
    		cell.addName(course.getTitle());
    	}
    	cell.setCommitted(notAvailable);
    	cell.setDay(day);
    	cell.setSlot(slot);
    	if (context.isShowCourseTitle() || context.isShowClassNameTwoLines()) {
    		cell.addTitle(assignment.getClazz().getClassLabel(course, context.isShowClassSuffix(), context.isShowConfigName()) +
    				(course.getTitle() != null && !course.getTitle().isEmpty() ? " - " + course.getTitle() : ""));
    	}

    	if (context.isShowCrossLists()) {
    		Set<CourseOffering> courses = assignment.getClazz().getSchedulingSubpart().getInstrOfferingConfig().getInstructionalOffering().getCourseOfferings();
    		if (courses.size() > 1) {
    			for (CourseOffering co: new TreeSet<CourseOffering>(courses)) {
    				if (co.isIsControl()) continue;
    				cell.addName(assignment.getClazz().getClassLabel(co, context.isShowClassSuffix(), context.isShowConfigName()));
    				if (context.isShowCourseTitle()) {
    					if (co.getTitle() != null && !co.getTitle().isEmpty()) {
    						cell.addName(co.getTitle());
    						cell.addTitle(assignment.getClazz().getClassLabel(co, context.isShowClassSuffix(), context.isShowConfigName()) + " - " + co.getTitle());
    					} else {
    						cell.addTitle(assignment.getClazz().getClassLabel(co, context.isShowClassSuffix(), context.isShowConfigName()));
    					}
    		    	} else if (context.isShowClassNameTwoLines()) {
    		    		cell.addTitle(assignment.getClazz().getClassLabel(co, context.isShowClassSuffix(), context.isShowConfigName()));
    		    	}
    			}
    		}
    	}

		cell.setLength(assignment.getTimePattern().getSlotsPerMtg());
		if (assignment.getTimePattern().getType() == TimePattern.sTypeExactTime) {
			DurationModel dm = assignment.getClazz().getSchedulingSubpart().getInstrOfferingConfig().getDurationModel();
			int minsPerMeeting = dm.getExactTimeMinutesPerMeeting(assignment.getClazz().getSchedulingSubpart().getMinutesPerWk(), assignment.getDatePattern(), assignment.getDays());
			cell.setLength(ExactTimeMins.getNrSlotsPerMtg(minsPerMeeting));
		}
		for (Location location: assignment.getRooms())
			cell.addRoom(location.getLabel());

		int bgMode = context.getBgMode();
		AssignmentPreferenceInfo assignmentInfo = null;
		if (notAvailable) {
			cell.setBackground(sBgColorNotAvailable);
		} else {
			try {
				assignmentInfo = (AssignmentPreferenceInfo)assignment.getAssignmentInfo("AssignmentInfo");
			} catch (Exception e) {
				Debug.error(e);
			}
		}

		if (assignmentInfo != null) {
			int roomPref = assignmentInfo.combineRoomPreference();
			if (model.getResourceType() == ResourceType.ROOM.ordinal() && model.getResourceId() != null)
				roomPref = assignmentInfo.getRoomPreference(model.getResourceId());
			switch (BgMode.values()[bgMode]) {
			case TimePref:
				cell.setBackground(pref2color(assignmentInfo.getTimePreference()));
				break;
			case RoomPref:
				cell.setBackground(pref2color(roomPref));
				break;
			case StudentConf:
				cell.setBackground(conflicts2color(assignmentInfo.getNrStudentConflicts()));
				break;
			case InstructorBtbPref:
				cell.setBackground(pref2color(assignmentInfo.getBtbInstructorPreference()));
				break;
			case Perturbations:
				String pref = PreferenceLevel.sNeutral;
				if (assignmentInfo.getInitialAssignment()!=null) {
					if (assignmentInfo.getIsInitial()) pref = PreferenceLevel.sStronglyPreferred;
					else if (assignmentInfo.getHasInitialSameTime()) pref = PreferenceLevel.sDiscouraged;
					else if (assignmentInfo.getHasInitialSameRoom()) pref = PreferenceLevel.sStronglyDiscouraged;
					else pref=PreferenceLevel.sProhibited;
				}
				cell.setBackground(pref2color(pref));
				break;
			case PerturbationPenalty:
				cell.setBackground(conflicts2color((int)Math.ceil(assignmentInfo.getPerturbationPenalty())));
				break;
			case HardConflicts:
				cell.setBackground(pref2color(hardConflicts2pref(assignmentInfo)));
				break;
			case DepartmentalBalancing:
				cell.setBackground(conflicts2colorFast((int)assignmentInfo.getMaxDeptBalancPenalty()));
				break;
			case TooBigRooms:
				//FIXME: this needs to be changed to reflect the new maxLimit/room ratio model
				int roomCap = assignment.getClazz().getMinRoomLimit().intValue();
				long minRoomSize = assignmentInfo.getMinRoomSize();
				int roomSize = 0;
				for (Iterator i=assignment.getRooms().iterator();i.hasNext();) {
					Location r = (Location)i.next();
					roomSize += r.getCapacity().intValue();
				}
				if (roomSize < roomCap)
					cell.setBackground(pref2color(PreferenceLevel.sRequired));
				else
					cell.setBackground(pref2color(assignmentInfo.getTooBigRoomPreference()));
				if (!assignment.getRooms().isEmpty()) {
					cell.setPreference((assignmentInfo.getNrRoomLocations() == 1 ? "" : "") +
							roomCap + " / " + (minRoomSize == Integer.MAX_VALUE ? "-" : String.valueOf(minRoomSize)) + " / " + roomSize +
							(assignmentInfo.getNrRoomLocations() == 1 ? "":""));
				}
				break;
			case StudentGroups:
				if (assignmentInfo.getStudentGroupPercent() != null)
					cell.setBackground(percentage2color(assignmentInfo.getStudentGroupPercent()));
				if (assignmentInfo.getStudentGroupComment() != null) {
					cell.setPreference(assignmentInfo.getStudentGroupComment());
				}
			}

			if (!cell.hasPreference()) {
				cell.setPreference(
					(assignmentInfo.getBestNormalizedTimePreference()<assignmentInfo.getNormalizedTimePreference()?""+(int)(assignmentInfo.getNormalizedTimePreference()-assignmentInfo.getBestNormalizedTimePreference())+"":""+(int)(assignmentInfo.getNormalizedTimePreference()-assignmentInfo.getBestNormalizedTimePreference())) + ", " +
					(assignmentInfo.getNrStudentConflicts()>0?""+assignmentInfo.getNrStudentConflicts()+"":""+assignmentInfo.getNrStudentConflicts()) + ", " +
					(assignmentInfo.getBestRoomPreference()<roomPref?""+(roomPref-assignmentInfo.getBestRoomPreference())+"":""+(roomPref-assignmentInfo.getBestRoomPreference()))
					);
			}
			cell.setProperty(Property.TimePreference, (int)assignmentInfo.getNormalizedTimePreference());
			cell.setProperty(Property.StudentConflicts,  assignmentInfo.getNrStudentConflicts());
			cell.setProperty(Property.StudentConflictsCommitted, assignmentInfo.getNrCommitedStudentConflicts());
			cell.setProperty(Property.StudentConflictsDistance, assignmentInfo.getNrDistanceStudentConflicts());
			cell.setProperty(Property.StudentConflictsHard, assignmentInfo.getNrHardStudentConflicts());
			cell.setProperty(Property.RoomPreference, roomPref);
			cell.setProperty(Property.InstructorPreference, assignmentInfo.getBtbInstructorPreference());
			if (assignmentInfo.getInitialAssignment() != null) {
				cell.setProperty(Property.InitialAssignment, assignmentInfo.getIsInitial() ? "-" : assignmentInfo.getInitialAssignment());
				cell.setProperty(Property.PerturbationPenalty, sDF.format(assignmentInfo.getPerturbationPenalty()));
			}
			cell.setProperty(Property.NonConflictingPlacements, assignmentInfo.getNrPlacementsNoConf());
			cell.setProperty(Property.DepartmentBalance, sDF.format(assignmentInfo.getDeptBalancPenalty()));
		} else {
			cell.setProperty(Property.Owner, assignment.getSolution().getOwner().getName());
		}

		if (bgMode == BgMode.DistributionConstPref.ordinal()) {
			Vector constraintInfos = new Vector();
			try {
				constraintInfos = assignment.getConstraintInfos("DistributionInfo");
			} catch (Exception e) {
				Debug.error(e);
			}
			if (constraintInfos!=null) {
				PreferenceCombination pref = PreferenceCombination.getDefault();
				for (Enumeration e=constraintInfos.elements();e.hasMoreElements();) {
					GroupConstraintInfo gcInfo = (GroupConstraintInfo)e.nextElement();
					if (gcInfo.isSatisfied()) continue;
					if (PreferenceLevel.sRequired.equals(gcInfo.getPreference()) || PreferenceLevel.sProhibited.equals(gcInfo.getPreference()))
						pref.addPreferenceProlog(PreferenceLevel.sProhibited);
					pref.addPreferenceInt(Math.abs(PreferenceLevel.prolog2int(gcInfo.getPreference())));
				}
				cell.setProperty(Property.DistributionPreference, pref.getPreferenceProlog());
				cell.setBackground(pref2color(pref.getPreferenceProlog()));
			}
		}

		if (ApplicationProperty.TimetableGridUseClassInstructors.isTrue()) {
			if (!ApplicationProperty.TimetableGridUseClassInstructorsCheckClassDisplayInstructors.isTrue() || assignment.getClazz().isDisplayInstructor()) {
				for (ClassInstructor instructor: assignment.getClazz().getClassInstructors()) {
					if (instructor.isLead() || ApplicationProperty.TimetableGridUseClassInstructorsCheckLead.isFalse())
						cell.addInstructor(instructor.getInstructor().getName(context.getInstructorNameFormat()));
				}
			}
		} else {
			for (DepartmentalInstructor instructor: assignment.getInstructors()) {
				cell.addInstructor(instructor.getName(context.getInstructorNameFormat()));
			}
		}

		String days = "";
        for (int i = 0; i < Constants.DAY_CODES.length; i++)
            if ((assignment.getDays() & Constants.DAY_CODES[i]) != 0)
            	days += CONST.shortDays()[i];
		cell.setDays(days);
		cell.setTime(
				Constants.toTime(assignment.getStartSlot() * Constants.SLOT_LENGTH_MIN + Constants.FIRST_SLOT_TIME_MIN) +
				" - " + Constants.toTime((assignment.getStartSlot() + assignment.getSlotPerMtg()) * Constants.SLOT_LENGTH_MIN + Constants.FIRST_SLOT_TIME_MIN - assignment.getBreakTime())
				);
		cell.setDate(formatDatePattern(assignment.getDatePattern(), Constants.DAY_CODES[day]));
		cell.setWeekCode(pattern2string(assignment.getDatePattern().getPatternBitSet()));
		return cell;
	}

    protected static double countUtilization(Iterable<Assignment> assignments, TimetableGridContext context) {
    	Set<Integer> slots = new HashSet<Integer>();
        for (Assignment assignment: assignments) {
        	TimeLocation t = (assignment == null ? null : assignment.getTimeLocation());
            if (t == null) continue;
            int start = Math.max(context.getFirstSlot(), t.getStartSlot());
            int stop = Math.min(context.getLastSlot(), t.getStartSlot() + t.getLength() - 1);
            if (start > stop) continue;
            if (context.getFirstDay() >= 0) {
                for (int idx = context.getFirstDay(); idx < 7 + context.getFirstDay(); idx ++) {
                	int dow = ((idx + context.getStartDayDayOfWeek()) % 7);
                	if (t.getWeekCode().get(idx) && (t.getDayCode() & Constants.DAY_CODES[dow]) != 0 && (context.getDayCode() & Constants.DAY_CODES[dow]) != 0) {
                		for (int slot = start; slot <= stop; slot ++)
                			slots.add(288 * idx + slot);
                	}
                }
            } else {
                int idx = -1;
                while ((idx = t.getWeekCode().nextSetBit(1 + idx)) >= 0) {
                	int dow = ((idx + context.getStartDayDayOfWeek()) % 7);
                	if (context.getDefaultDatePattern().get(idx) && (t.getDayCode() & Constants.DAY_CODES[dow]) != 0 && (context.getDayCode() & Constants.DAY_CODES[dow]) != 0) {
                		for (int slot = start; slot <= stop; slot ++)
                			slots.add(288 * idx + slot);
                	}
                }
            }
        }
        return slots.size() / (context.getNumberOfWeeks() * 12);
    }

    public static String formatDatePattern(DatePattern dp, int dayCode) {
    	if (dp == null || dp.isDefault()) return null;
    	String format = ApplicationProperty.DatePatternFormatUseDates.value();
    	if ("never".equals(format)) return dp.getName();
    	if ("extended".equals(format) && dp.getType() != DatePattern.sTypeExtended) return dp.getName();
    	if ("alternate".equals(format) && dp.getType() == DatePattern.sTypeAlternate) return dp.getName();
    	BitSet weekCode = dp.getPatternBitSet();
    	if (weekCode.isEmpty()) return dp.getName();
    	Calendar cal = Calendar.getInstance(Locale.US); cal.setLenient(true);
    	Date dpFirstDate = DateUtils.getDate(1, dp.getSession().getPatternStartMonth(), dp.getSession().getSessionStartYear());
    	cal.setTime(dpFirstDate);
    	int idx = weekCode.nextSetBit(0);
    	cal.add(Calendar.DAY_OF_YEAR, idx);
    	Date first = null;
    	while (idx < weekCode.size() && first == null) {
    		if (weekCode.get(idx)) {
        		int dow = cal.get(Calendar.DAY_OF_WEEK);
        		switch (dow) {
        		case Calendar.MONDAY:
        			if ((dayCode & DayCode.MON.getCode()) != 0) first = cal.getTime();
        			break;
        		case Calendar.TUESDAY:
        			if ((dayCode & DayCode.TUE.getCode()) != 0) first = cal.getTime();
        			break;
        		case Calendar.WEDNESDAY:
        			if ((dayCode & DayCode.WED.getCode()) != 0) first = cal.getTime();
        			break;
        		case Calendar.THURSDAY:
        			if ((dayCode & DayCode.THU.getCode()) != 0) first = cal.getTime();
        			break;
        		case Calendar.FRIDAY:
        			if ((dayCode & DayCode.FRI.getCode()) != 0) first = cal.getTime();
        			break;
        		case Calendar.SATURDAY:
        			if ((dayCode & DayCode.SAT.getCode()) != 0) first = cal.getTime();
        			break;
        		case Calendar.SUNDAY:
        			if ((dayCode & DayCode.SUN.getCode()) != 0) first = cal.getTime();
        			break;
        		}
        	}
    		cal.add(Calendar.DAY_OF_YEAR, 1); idx++;
    	}
    	if (first == null) return dp.getName();
    	cal.setTime(dpFirstDate);
    	idx = weekCode.length() - 1;
    	cal.add(Calendar.DAY_OF_YEAR, idx);
    	Date last = null;
    	while (idx >= 0 && last == null) {
    		if (weekCode.get(idx)) {
        		int dow = cal.get(Calendar.DAY_OF_WEEK);
        		switch (dow) {
        		case Calendar.MONDAY:
        			if ((dayCode & DayCode.MON.getCode()) != 0) last = cal.getTime();
        			break;
        		case Calendar.TUESDAY:
        			if ((dayCode & DayCode.TUE.getCode()) != 0) last = cal.getTime();
        			break;
        		case Calendar.WEDNESDAY:
        			if ((dayCode & DayCode.WED.getCode()) != 0) last = cal.getTime();
        			break;
        		case Calendar.THURSDAY:
        			if ((dayCode & DayCode.THU.getCode()) != 0) last = cal.getTime();
        			break;
        		case Calendar.FRIDAY:
        			if ((dayCode & DayCode.FRI.getCode()) != 0) last = cal.getTime();
        			break;
        		case Calendar.SATURDAY:
        			if ((dayCode & DayCode.SAT.getCode()) != 0) last = cal.getTime();
        			break;
        		case Calendar.SUNDAY:
        			if ((dayCode & DayCode.SUN.getCode()) != 0) last = cal.getTime();
        			break;
        		}
        	}
    		cal.add(Calendar.DAY_OF_YEAR, -1); idx--;
    	}
    	if (last == null) return dp.getName();
        Formats.Format<Date> dpf = Formats.getDateFormat(Formats.Pattern.DATE_EVENT_SHORT);
    	return dpf.format(first) + (first.equals(last) ? "" : " - " + dpf.format(last));
    }

    public static TimetableGridModel createModel(String solutionIdsStr, Location room, org.hibernate.Session hibSession, TimetableGridContext context) {
    	TimetableGridModel model = new TimetableGridModel(ResourceType.ROOM.ordinal(), room.getUniqueId());
    	model.setName(room.getLabel());
    	model.setSize(room.getCapacity().intValue());
    	model.setFirstDay(context.getFirstDay());
    	model.setFirstSessionDay(context.getFirstSessionDay());
    	model.setFirstDate(context.getFirstDate());
    	Solution firstSolution = null;
		String ownerIds = "";
		HashSet deptIds = new HashSet();
		for (StringTokenizer s=new StringTokenizer(solutionIdsStr,",");s.hasMoreTokens();) {
			Long solutionId = Long.valueOf(s.nextToken());
			Solution solution = SolutionDAO.getInstance().get(solutionId, hibSession);
			if (solution==null) continue;
			if (firstSolution==null) firstSolution = solution;
			if (ownerIds.length()>0) ownerIds += ",";
			ownerIds += solution.getOwner().getUniqueId();
			for (Iterator i=solution.getOwner().getDepartments().iterator();i.hasNext();) {
				Department d = (Department)i.next();
				deptIds.add(d.getUniqueId());
			}
		}
		Query q = hibSession.createQuery("select distinct a from Assignment as a inner join a.rooms as r where a.solution.uniqueId in ("+solutionIdsStr+") and r.uniqueId=:resourceId");
		q.setLong("resourceId", room.getUniqueId());
		q.setCacheable(true);
		List<Assignment> assignments = q.list();
		createCells(model, assignments, hibSession, context, false);
		q = hibSession.createQuery("select distinct a from Room r inner join r.assignments as a "+
				"where r.uniqueId=:roomId and a.solution.commited=true and a.solution.owner.session.uniqueId=:sessionId and a.solution.owner.uniqueId not in ("+ownerIds+")");
		q.setLong("roomId",room.getUniqueId());
        q.setLong("sessionId", room.getSession().getUniqueId().longValue());
		q.setCacheable(true);
		List<Assignment> committed = q.list();
		createCells(model, committed, hibSession, context, true);
		model.setUtilization(countUtilization(new Combine<Assignment>(assignments, committed), context));
		RoomSharingModel sharing = room.getRoomSharingModel();
		if (sharing != null) {
			for (int i = 0; i < Constants.DAY_CODES.length; i++) {
				int start = 0; Boolean av = null;
				for (int j = 0; j < Constants.SLOTS_PER_DAY; j++) {
					Boolean available;
					if (sharing.isFreeForAll(i,j)) {
						available = true;
					} else if (sharing.isNotAvailable(i,j)) {
						available = false;
					} else {
						Long dept = sharing.getDepartmentId(i,j);
						available = (dept == null || deptIds.contains(dept));
					}
					if (av == null) {
						av = available; start = j;
					} else if (!av.equals(available)) {
						if (!av) {
							TimetableGridBackground bg = new TimetableGridBackground();
							bg.setBackground(sBgColorNotAvailable);
							bg.setSlot(start);
							bg.setLength(j - start);
							bg.setDay(i);
							bg.setAvailable(false);
							model.addBackground(bg);
						}
						av = available; start = j;
					}
				}
				if (av != null && !av) {
					TimetableGridBackground bg = new TimetableGridBackground();
					bg.setBackground(sBgColorNotAvailable);
					bg.setSlot(start);
					bg.setLength(Constants.SLOTS_PER_DAY - start);
					bg.setDay(i);
					bg.setAvailable(false);
					model.addBackground(bg);
				}
			}
		}
		if (context.isShowEvents() && RoomAvailability.getInstance() != null) {
			createMeetingCells(model, room.getSession(), context,
					RoomAvailability.getInstance().getRoomAvailability(room.getUniqueId(), context.getSessionStartDate(), context.getSessionEndDate(), RoomAvailabilityInterface.sClassType),
					room.getLabel());
		}
        model.setType(room instanceof Room ? ((Room)room).getRoomType().getUniqueId(): null);
        return model;
	}

    public static TimetableGridModel createModel(String solutionIdsStr, DepartmentalInstructor instructor, org.hibernate.Session hibSession, TimetableGridContext context) {
    	TimetableGridModel model = new TimetableGridModel(ResourceType.INSTRUCTOR.ordinal(), instructor.getUniqueId());
    	model.setName(instructor.getName(NameFormat.LAST_FIRST_MIDDLE.reference()));
    	model.setFirstDay(context.getFirstDay());
    	model.setFirstSessionDay(context.getFirstSessionDay());
    	model.setFirstDate(context.getFirstDate());

    	Solution firstSolution = null;
		String ownerIds = "";
		for (StringTokenizer s=new StringTokenizer(solutionIdsStr,",");s.hasMoreTokens();) {
			Long solutionId = Long.valueOf(s.nextToken());
			Solution solution = (new SolutionDAO()).get(solutionId, hibSession);
			if (solution==null) continue;
			if (firstSolution==null) firstSolution = solution;
			if (ownerIds.length()>0) ownerIds += ",";
			ownerIds += solution.getOwner().getUniqueId();
		}

		List<Assignment> assignments = null;
		List<Assignment> committed = null;
		if (ApplicationProperty.TimetableGridUseClassInstructors.isTrue()) {
			String check = "";
			if (ApplicationProperty.TimetableGridUseClassInstructorsCheckLead.isTrue())
				check += " and i.lead = true";
			if (ApplicationProperty.TimetableGridUseClassInstructorsCheckClassDisplayInstructors.isTrue())
				check += " and i.classInstructing.displayInstructor = true";
			if (instructor.getExternalUniqueId() != null && !instructor.getExternalUniqueId().isEmpty()) {
				Query q = hibSession.createQuery("select distinct a from Assignment as a inner join a.clazz.classInstructors as i where a.solution.uniqueId in (" + solutionIdsStr + ") and i.instructor.externalUniqueId = :extId" + check);
				q.setString("extId", instructor.getExternalUniqueId());
				q.setCacheable(true);
				assignments = q.list();
				q = hibSession.createQuery("select distinct a from ClassInstructor i inner join i.classInstructing.assignments as a "+
						"where i.instructor.externalUniqueId = :extId and a.solution.commited = true and a.solution.owner.session.uniqueId = :sessionId and a.solution.owner.uniqueId not in (" + ownerIds + ")" + check);
				q.setString("extId",instructor.getExternalUniqueId());
	            q.setLong("sessionId", instructor.getDepartment().getSession().getUniqueId().longValue());
				q.setCacheable(true);
				committed = q.list();
			} else {
				Query q = hibSession.createQuery("select distinct a from Assignment as a inner join a.clazz.classInstructors as i where a.solution.uniqueId in (" + solutionIdsStr + ") and i.instructor.uniqueId = :instructorId" + check);
				q.setLong("instructorId", instructor.getUniqueId());
				q.setCacheable(true);
				assignments = q.list();
				q = hibSession.createQuery("select distinct a from ClassInstructor i inner join i.classInstructing.assignments as a "+
						"where i.instructor.uniqueId = :instructorId and a.solution.commited = true and a.solution.owner.session.uniqueId = :sessionId and a.solution.owner.uniqueId not in (" + ownerIds + ")" + check);
				q.setLong("instructorId",instructor.getUniqueId());
	            q.setLong("sessionId", instructor.getDepartment().getSession().getUniqueId().longValue());
				q.setCacheable(true);
				committed = q.list();
			}
		} else {
			if (instructor.getExternalUniqueId()!=null && instructor.getExternalUniqueId().length()>0) {
				Query q = hibSession.createQuery("select distinct a from Assignment as a inner join a.instructors as i where a.solution.uniqueId in ("+solutionIdsStr+") and i.externalUniqueId=:puid");
				q.setString("puid", instructor.getExternalUniqueId());
				q.setCacheable(true);
				assignments = q.list();
				q = hibSession.createQuery("select distinct a from DepartmentalInstructor i inner join i.assignments as a "+
						"where i.externalUniqueId=:puid and a.solution.commited=true and a.solution.owner.session.uniqueId=:sessionId and a.solution.owner.uniqueId not in ("+ownerIds+")");
				q.setString("puid",instructor.getExternalUniqueId());
	            q.setLong("sessionId", instructor.getDepartment().getSession().getUniqueId().longValue());
				q.setCacheable(true);
				committed = q.list();
			} else {
				Query q = hibSession.createQuery("select distinct a from Assignment as a inner join a.instructors as i where a.solution.uniqueId in ("+solutionIdsStr+") and i.uniqueId=:resourceId");
				q.setLong("resourceId", instructor.getUniqueId());
				q.setCacheable(true);
				assignments = q.list();
				q = hibSession.createQuery("select distinct a from DepartmentalInstructor i inner join i.assignments as a "+
						"where i.uniqueId=:instructorId and a.solution.commited=true and a.solution.owner.session.uniqueId=:sessionId and a.solution.owner.uniqueId not in ("+ownerIds+")");
				q.setLong("instructorId",instructor.getUniqueId());
	            q.setLong("sessionId", instructor.getDepartment().getSession().getUniqueId().longValue());
				q.setCacheable(true);
				committed = q.list();
			}
		}

		createCells(model, assignments, hibSession, context, false);
		createCells(model, committed, hibSession, context, true);
		model.setUtilization(countUtilization(new Combine<Assignment>(assignments, committed), context));

		if (context.isShowEvents() && RoomAvailability.getInstance() != null) {
			createMeetingCells(model, instructor.getDepartment().getSession(), context,
					RoomAvailability.getInstance().getInstructorAvailability(instructor.getUniqueId(), context.getSessionStartDate(), context.getSessionEndDate(), RoomAvailabilityInterface.sClassType),
					null);
		}
        if (instructor.getPositionType()!=null)
            model.setType(new Long(instructor.getPositionType().getSortOrder()));

    	return model;
    }

    public static TimetableGridModel createModel(String solutionIdsStr, Department department, org.hibernate.Session hibSession, TimetableGridContext context) {
    	TimetableGridModel model = new TimetableGridModel(ResourceType.DEPARTMENT.ordinal(), department.getUniqueId());
    	model.setName(department.getShortLabel());
    	model.setFirstDay(context.getFirstDay());
    	model.setFirstSessionDay(context.getFirstSessionDay());
    	model.setFirstDate(context.getFirstDate());

    	Query q = hibSession.createQuery("select distinct a from Assignment as a inner join a.clazz.schedulingSubpart.instrOfferingConfig.instructionalOffering.courseOfferings as o inner join o.subjectArea.department as d where " +
				"a.solution.uniqueId in ("+solutionIdsStr+") and d.uniqueId=:resourceId and " +
				"o.isControl=true");
		q.setCacheable(true);
		q.setLong("resourceId", department.getUniqueId());
		List assignments = q.list();
		createCells(model, assignments, hibSession, context, false);

		model.setSize(assignments.size());
		model.setUtilization(countUtilization(assignments, context));

		return model;
    }

    public static TimetableGridModel createModel(String solutionIdsStr, SubjectArea sa, org.hibernate.Session hibSession, TimetableGridContext context) {
    	TimetableGridModel model = new TimetableGridModel(ResourceType.SUBJECT_AREA.ordinal(), sa.getUniqueId());
    	model.setName(sa.getSubjectAreaAbbreviation());
    	model.setFirstDay(context.getFirstDay());
    	model.setFirstSessionDay(context.getFirstSessionDay());
    	model.setFirstDate(context.getFirstDate());

    	Query q = hibSession.createQuery("select distinct a from Assignment as a inner join a.clazz.schedulingSubpart.instrOfferingConfig.instructionalOffering.courseOfferings as o inner join o.subjectArea as sa where " +
				"a.solution.uniqueId in ("+solutionIdsStr+") and sa.uniqueId=:resourceId and " +
				"o.isControl=true");
		q.setCacheable(true);
		q.setLong("resourceId", sa.getUniqueId());
		List assignments = q.list();

		createCells(model, assignments, hibSession, context, false);
		model.setSize(assignments.size());
		model.setUtilization(countUtilization(assignments, context));

		return model;
    }

    public static TimetableGridModel createModel(String solutionIdsStr, CurriculumClassification cc, org.hibernate.Session hibSession, TimetableGridContext context) {
    	TimetableGridModel model = new TimetableGridModel(ResourceType.CURRICULUM.ordinal(), cc.getUniqueId());
    	model.setName(cc.getCurriculum().getAbbv() + " " + cc.getName());
    	model.setFirstDay(context.getFirstDay());
    	model.setFirstSessionDay(context.getFirstSessionDay());
    	model.setFirstDate(context.getFirstDate());

		Query q = hibSession.createQuery("select distinct a from CurriculumClassification cc inner join cc.courses cx, Assignment a inner join a.clazz.schedulingSubpart.instrOfferingConfig.instructionalOffering.courseOfferings co where " +
				"a.solution.uniqueId in ("+solutionIdsStr+") and cc.uniqueId=:resourceId and " +
				"cx.course = co");
		q.setCacheable(true);
		q.setLong("resourceId", cc.getUniqueId());
		List assignments = q.list();
		Map<Long, Set<Long>[]> restrictions = new Hashtable<Long, Set<Long>[]>();
		for (Object[] o: (List<Object[]>)hibSession.createQuery(
				"select distinct cc.course.instructionalOffering.uniqueId, (case when g.uniqueId is null then x.uniqueId else g.uniqueId end), z.uniqueId " +
				"from CurriculumReservation r left outer join r.configurations g left outer join r.classes z left outer join z.schedulingSubpart.instrOfferingConfig x " +
				"left outer join r.majors rm left outer join r.classifications rc, " +
				"CurriculumCourse cc inner join cc.classification.curriculum.majors cm " +
				"where cc.classification.uniqueId = :resourceId " +
				"and cc.course.instructionalOffering = r.instructionalOffering and r.area = cc.classification.curriculum.academicArea "+
				"and (rm is null or rm = cm) and (rc is null or rc = cc.classification.academicClassification)")
				.setLong("resourceId", cc.getUniqueId()).setCacheable(true).list()) {
			Long offeringId = (Long)o[0];
			Long configId = (Long)o[1];
			Long clazzId = (Long)o[2];
			Set<Long>[] r = restrictions.get(offeringId);
			if (r == null) {
				r = new Set[] { new HashSet<Long>(), new HashSet<Long>()};
				restrictions.put(offeringId, r);
			}
			if (configId != null) r[0].add(configId);
			if (clazzId != null) r[1].add(clazzId);
		}
		if (!restrictions.isEmpty())
			for (Iterator i = assignments.iterator(); i.hasNext(); ) {
				Assignment asgn = (Assignment)i.next();
				Set<Long>[] r = (restrictions == null ? null : restrictions.get(asgn.getClazz().getSchedulingSubpart().getInstrOfferingConfig().getInstructionalOffering().getUniqueId()));
	    		if (r != null && EventLookupBackend.hide(r, asgn.getClazz())) i.remove();
			}

		createCells(model, assignments, hibSession, context, false);
		model.setSize(assignments.size());
		model.setUtilization(countUtilization(assignments, context));

		return model;
    }

    public static TimetableGridModel createModel(String solutionIdsStr, StudentGroup g, org.hibernate.Session hibSession, TimetableGridContext context) {
    	TimetableGridModel model = new TimetableGridModel(ResourceType.STUDENT_GROUP.ordinal(), g.getUniqueId());
    	model.setName(g.getGroupAbbreviation());
    	model.setFirstDay(context.getFirstDay());
    	model.setFirstSessionDay(context.getFirstSessionDay());
    	model.setFirstDate(context.getFirstDate());

    	Query q = hibSession.createQuery(
				"select distinct a from StudentGroupReservation r, Assignment a inner join a.clazz.schedulingSubpart.instrOfferingConfig.instructionalOffering as io where "+
				"a.solution.uniqueId in ("+solutionIdsStr+") and io = r.instructionalOffering and r.group.uniqueId=:resourceId");
		q.setCacheable(true);
		q.setLong("resourceId", g.getUniqueId());
		List assignments = q.list();
		Map<Long, Set<Long>[]> restrictions = new Hashtable<Long, Set<Long>[]>();
		for (Object[] o: (List<Object[]>)hibSession.createQuery(
				"select distinct r.instructionalOffering.uniqueId, (case when g.uniqueId is null then x.uniqueId else g.uniqueId end), z.uniqueId " +
				"from StudentGroupReservation r left outer join r.configurations g left outer join r.classes z left outer join z.schedulingSubpart.instrOfferingConfig x " +
				"where r.group.uniqueId = :resourceId")
				.setLong("resourceId", g.getUniqueId()).setCacheable(true).list()) {
			Long offeringId = (Long)o[0];
			Long configId = (Long)o[1];
			Long clazzId = (Long)o[2];
			Set<Long>[] r = restrictions.get(offeringId);
			if (r == null) {
				r = new Set[] { new HashSet<Long>(), new HashSet<Long>()};
				restrictions.put(offeringId, r);
			}
			if (configId != null) r[0].add(configId);
			if (clazzId != null) r[1].add(clazzId);
		}
		if (!restrictions.isEmpty())
			for (Iterator i = assignments.iterator(); i.hasNext(); ) {
				Assignment asgn = (Assignment)i.next();
				Set<Long>[] r = (restrictions == null ? null : restrictions.get(asgn.getClazz().getSchedulingSubpart().getInstrOfferingConfig().getInstructionalOffering().getUniqueId()));
	    		if (r != null && EventLookupBackend.hide(r, asgn.getClazz())) i.remove();
			}

		createCells(model, assignments, hibSession, context, false);
		model.setSize(assignments.size());
		model.setUtilization(countUtilization(assignments, context));

		return model;
    }

    public static TimetableGridModel createModel(String solutionIdsStr, StudentGroupInfo g, org.hibernate.Session hibSession, TimetableGridContext context) {
    	TimetableGridModel model = new TimetableGridModel(ResourceType.STUDENT_GROUP.ordinal(), g.getGroupId());
    	model.setName(g.getGroupName());
    	model.setFirstDay(context.getFirstDay());
    	model.setFirstSessionDay(context.getFirstSessionDay());
    	model.setFirstDate(context.getFirstDate());

		List<Long> classIds = new ArrayList<Long>();
		for (StudentGroupInfo.ClassInfo clazz: g.getGroupAssignments())
			classIds.add(clazz.getClassId());
		if (classIds.isEmpty()) return null;

		Query q = hibSession.createQuery(
				"select distinct a from Assignment a where a.solution.uniqueId in ("+solutionIdsStr+") and a.classId in (:classIds)");
		q.setParameterList("classIds", classIds, new LongType());
		q.setCacheable(true);
		List assignments = q.list();
		model.setSize((int)Math.round(g.countStudentWeights()));

		for (Iterator i = assignments.iterator(); i.hasNext(); ) {
			Assignment assignment = (Assignment)i.next();
			List<TimetableGridCell> cells = createCells(model, assignment, hibSession, context, false);
			StudentGroupInfo.ClassInfo ci = g.getGroupAssignment(assignment.getClassId());
			if (ci != null) {
				int total = g.countStudentsOfOffering(assignment.getClazz().getSchedulingSubpart().getInstrOfferingConfig().getInstructionalOffering().getUniqueId());
				for (TimetableGridCell cell: cells) {
					cell.setGroup("(" + Math.round(ci.countStudentsWeight()) + ")");
					if (ci.getStudents() != null && !ci.getStudents().isEmpty() && total > 1) {
						int assigned = ci.getStudents().size();
						int minLimit = assignment.getClazz().getExpectedCapacity();
	                	int maxLimit = assignment.getClazz().getMaxExpectedCapacity();
	                	int limit = maxLimit;
	                	if (minLimit < maxLimit) {
	                		int roomLimit = (int) Math.floor(assignment.getPlacement().getRoomSize() / (assignment.getClazz().getRoomRatio() == null ? 1.0f : assignment.getClazz().getRoomRatio()));
	                		// int roomLimit = Math.round((c.getRoomRatio() == null ? 1.0f : c.getRoomRatio()) * p.getRoomSize());
	                		limit = Math.min(Math.max(minLimit, roomLimit), maxLimit);
	                	}
	                    if (assignment.getClazz().getSchedulingSubpart().getInstrOfferingConfig().isUnlimitedEnrollment() || limit >= 9999) limit = Integer.MAX_VALUE;
						int p = 100 * assigned / Math.min(limit, total);
						cell.setBackground(percentage2color(p));
						cell.setPreference(assigned + " of " + total);
					}
				}
			}
		}
		model.setUtilization(g.getGroupValue());

		return model;
    }

}


text:
package me.ztiany.mybatis.test.spring;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.annotation.Resource;

import me.ztiany.mybatis.pojo.User;
import me.ztiany.mybatis.test.basic.mapper.UserMapper;

/**
 * 测试Mapper配置
 *
 * @author Ztiany
 * Email ztiany3@gmail.com
 * Date 18.5.27 18:44
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring_mapper.xml")
public class SpringMapperTest {

    @Resource(name = "userMapper")
    private UserMapper mUserMapper;

    @Test
    public void testMapper() {
        Integer countUser = mUserMapper.countUser();
        System.out.println("countUser: " + countUser);
    }

    /**
     * 内部使用的是org.mybatis.spring.SqlSessionTemplate,会自动提交事务
     */
    @Test
    public void testInsertUser() {
        //执行Sql语句
        User user = new User();
        user.setUsername("古天乐");
        user.setBirthday(new Date());
        user.setAddress("香港");
        user.setSex("男");

        //返回插入后,影响的行数
        int insert = mUserMapper.insertUser(user);

        System.out.println("insert: " + insert);
        System.out.println("last insert user: " + user.getId());
    }

    /**
     * 在单表批量插入时,使用MyBatis的事务管理
     */
    @Test
    public void testBatchInsertUser() {
        List<User> userList = new ArrayList<>();

        User user1 = new User();
        user1.setUsername("小李");
        user1.setBirthday(new Date());
        user1.setAddress("深圳");
        user1.setSex("男");

        User user2 = new User();
        //user1.setUsername("小李"); //模拟插入失败
        user2.setBirthday(new Date());
        user2.setAddress("广东");
        user2.setSex("男");

        userList.add(user1);
        userList.add(user2);

        //返回插入后,影响的行数
        int insert = mUserMapper.batchInsert(userList);

        System.out.println("insert: " + insert);
        System.out.println("last insert user1: " + user1.getId());
    }

}


text:
const argv = require('yargs').argv;
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const debug = require('debug')('app:config:webpack');
const config = require('../config');

const paths = config.pathUtil;
const __DEV__ = config.compilerGlobals.__DEV__;
const __PROD__ = config.compilerGlobals.__PROD__;
const __TEST__ = config.compilerGlobals.__TEST__;

debug('Init webpack config.');
const mainEntry = [];
if (__DEV__) {
  mainEntry.push('react-hot-loader/patch');
  mainEntry.push(`webpack-hot-middleware/client.js?path=${config.compilerPublicPath}__webpack_hmr&reload=true`)
}
mainEntry.push(paths.client('main'));
const webpackConfig = {
  mode: __DEV__ ? 'development' : 'production',
  devtool: config.compilerSourceMap,
  entry: {
    main: mainEntry,
    vendor: config.compilerVendors
  },
  output: {
    path: paths.dist(),
    filename: __DEV__ ? '[name].bundle.js' : `[name].[${config.compilerHashType}].bundle.js`,
    publicPath: config.compilerPublicPath
  },
  resolve: {
    modules: [
      paths.client(),
      'node_modules'
    ],
    extensions: ['*', '.js', '.jsx', '.json', '.scss']
    // alias: {}
  },
  externals: {},
  module: {
    // noParse: /jquery/,
    rules: []
  },
  plugins: [
    new webpack.DefinePlugin(config.compilerGlobals),
    // new webpack.ProvidePlugin({})
  ],
  optimization: {}
};

// ------------------------------------
// Module Rules
// ------------------------------------
// JavaScript
webpackConfig.module.rules.push({
  test: /\.(js|jsx)$/,
  exclude: /node_modules/,
  use: [{
    loader: 'babel-loader',
    options: {
      cacheDirectory: true,
      presets: [
        'babel-preset-react',
        ['babel-preset-env', {
          modules: false,
          targets: {
            browsers: 'last 2 versions',
            uglify: true
          }
        }]
      ],
      plugins: [
        ['lodash', { 'id': ['lodash', 'semantic-ui-react'] }],
        //'babel-plugin-syntax-dynamic-import',
        'babel-plugin-transform-class-properties',
        ['babel-plugin-transform-runtime', {
          polyfill: false // only polyfill needed features in client/normalize.js
        }],
        ['babel-plugin-transform-object-rest-spread', {
          useBuiltIns: true // polyfill Object.assign in client/normalize.js
        }]
      ]
    }
  }]
});

// Images
webpackConfig.module.rules.push({
  test: /\.(jpe?g|png|gif)$/i,
  loader: 'url-loader',
  options: { limit: 8192 }
})

// Fonts
;[
  ['woff', 'application/font-woff'],
  ['woff2', 'application/font-woff2'],
  ['otf', 'font/opentype'],
  ['ttf', 'application/octet-stream'],
  ['eot', 'application/vnd.ms-fontobject'],
  ['svg', 'image/svg+xml'],
].forEach((font) => {
  const extension = font[0];
  const mimetype = font[1];

  webpackConfig.module.rules.push({
    test    : new RegExp(`\\.${extension}$`),
    loader  : 'url-loader',
    options : {
      name  : 'fonts/[name].[ext]',
      limit : 10000,
      mimetype,
    },
  })
});

// Styles
const cssLoader = {
  loader: 'css-loader',
  options: {
    modules: config.compilerCssModules,
    importLoaders: 1,
    localIdentName: '[name]__[local]--[hash:base64:5]',
    sourceMap: !!config.compilerSourceMap,
    minimize: {
      preset: ['default', {
        autoprefixer: { browsers: ['last 2 versions'] },
        discardComments: { removeAll : true },
        discardUnused: false,
        mergeIdents: false,
        reduceIdents: false,
        safe: true,
        sourcemap: !!config.compilerSourceMap
      }]
    }
  }
};

const sassLoader = {
  loader: 'sass-loader',
  options: {
    sourceMap: !!config.compilerSourceMap,
    includePaths: [
      paths.client('styles'),
    ]
  }
};

const extractStyles = new ExtractTextPlugin({
  filename: '[name].[contenthash].css',
  allChunks: true,
  disable: __DEV__
});

webpackConfig.module.rules.push({
  test: /\.(sass|scss)$/,
  loader: extractStyles.extract({
    fallback: 'style-loader',
    use: [
      cssLoader,
      sassLoader
    ]
  })
});

webpackConfig.module.rules.push({
  test: /\.css$/,
  loader: extractStyles.extract({
    fallback: 'style-loader',
    use: [cssLoader]
  })
});

webpackConfig.plugins.push(extractStyles);

// HTML Template
webpackConfig.plugins.push(new HtmlWebpackPlugin({
  template: paths.client('index.html'),
  favicon: paths.client('statics/favicon.ico'),
  hash: false,
  inject: 'body',
  minify: {
    collapseWhitespace: true
  }
}));

// Development Tools
if (__DEV__) {
  debug('Enable plugins for live development (HMR, NamedModulesPlugin).');
  webpackConfig.plugins.push(
    new webpack.HotModuleReplacementPlugin(),
  );
  webpackConfig.optimization['namedModules'] = true
}

// Production Optimizations
if (__PROD__) {
  debug('Enable plugins for production optimization.');
  webpackConfig.plugins.push(
    new webpack.LoaderOptionsPlugin({
      minimize: true,
      debug: false,
    }),
    new webpack.optimize.UglifyJsPlugin({
      sourceMap: !!config.devtool,
      comments: false,
      compress: {
        warnings: false,
        screw_ie8: true,
        conditionals: true,
        unused: true,
        comparisons: true,
        sequences: true,
        dead_code: true,
        evaluate: true,
        if_return: true,
        join_vars: true,
      }
    })
  );
  webpackConfig.optimization['noEmitOnErrors'] = true;
  webpackConfig.optimization['concatenateModules'] = true;
}

// Bundle Splitting
if (!__TEST__) {
  debug('Enable plugins for bundle split.');
  webpackConfig.optimization['splitChunks'] = {
    name: 'vendor',
    minChunks: 2
  };
}

/* Ensure that the compiler exits on errors during testing so that
they do not get skipped and misreported.*/
if (__TEST__ && !argv.watch) {
  webpackConfig.plugins.push(function () {
    this.plugin('done', function (stats) {
      if (stats.compilation.errors.length) {
        throw new Error(
          stats.compilation.errors.map(err => err.message || err)
        )
      }
    })
  })
}

module.exports = webpackConfig;


text:
'use strict';
// [START iot_http_includes]
const fs = require('fs');
const jwt = require('jsonwebtoken');
const request = require('retry-request');
// [END iot_http_includes]

console.log('Google Cloud IoT Core HTTP example.');
var argv = require(`yargs`)
  .options({
    projectId: {
      default: process.env.GCLOUD_PROJECT || process.env.GOOGLE_CLOUD_PROJECT,
      description: 'The Project ID to use. Defaults to the value of the GCLOUD_PROJECT or GOOGLE_CLOUD_PROJECT environment variables.',
      requiresArg: true,
      type: 'string'
    },
    cloudRegion: {
      default: 'us-central1',
      description: 'GCP cloud region.',
      requiresArg: true,
      type: 'string'
    },
    registryId: {
      description: 'Cloud IoT registry ID.',
      requiresArg: true,
      demandOption: true,
      type: 'string'
    },
    deviceId: {
      description: 'Cloud IoT device ID.',
      requiresArg: true,
      demandOption: true,
      type: 'string'
    },
    privateKeyFile: {
      description: 'Path to private key file.',
      requiresArg: true,
      demandOption: true,
      type: 'string'
    },
    algorithm: {
      description: 'Encryption algorithm to generate the RSA or EC JWT.',
      requiresArg: true,
      demandOption: true,
      choices: ['RS256', 'ES256'],
      type: 'string'
    },
    numMessages: {
      default: 100,
      description: 'Number of messages to publish.',
      requiresArg: true,
      type: 'number'
    },
    tokenExpMins: {
      default: 20,
      description: 'Minutes to JWT token expiration.',
      requiresArg: true,
      type: 'number'
    },
    httpBridgeAddress: {
      default: 'cloudiotdevice.googleapis.com',
      description: 'HTTP bridge address.',
      requiresArg: true,
      type: 'string'
    },
    messageType: {
      default: 'events',
      description: 'Message type to publish.',
      requiresArg: true,
      choices: ['events', 'state'],
      type: 'string'
    }
  })
  .example(`node $0 cloudiotHttp_example_nodejs.js --projectId=blue-jet-123 --registryId=my-registry --deviceId=my-node-device --privateKeyFile=../rsaPrivate.pem --algorithm=RS256`)
  .wrap(120)
  .recommendCommands()
  .epilogue(`For more information, see https://cloud.google.com/iot-core/docs`)
  .help()
  .strict()
  .argv;

// [START iot_http_variables]
// A unique string that identifies this device. For Google Cloud IoT Core, it
// must be in the format below.

let iatTime = parseInt(Date.now() / 1000);
let authToken = createJwt(argv.projectId, argv.privateKeyFile, argv.algorithm);
const devicePath = `projects/${argv.projectId}/locations/${argv.cloudRegion}/registries/${argv.registryId}/devices/${argv.deviceId}`;

// The request path, set accordingly depending on the message type.
const pathSuffix = argv.messageType === 'events'
  ? ':publishEvent' : ':setState';
const urlBase = `https://${argv.httpBridgeAddress}/v1/${devicePath}`;
const url = `${urlBase}${pathSuffix}`;
// [END iot_http_variables]

// Create a Cloud IoT Core JWT for the given project ID, signed with the given
// private key.
// [START iot_http_jwt]
function createJwt (projectId, privateKeyFile, algorithm) {
  // Create a JWT to authenticate this device. The device will be disconnected
  // after the token expires, and will have to reconnect with a new token. The
  // audience field should always be set to the GCP project ID.
  const token = {
    'iat': parseInt(Date.now() / 1000),
    'exp': parseInt(Date.now() / 1000) + 20 * 60, // 20 minutes
    'aud': projectId
  };
  const privateKey = fs.readFileSync(privateKeyFile);
  return jwt.sign(token, privateKey, { algorithm: algorithm });
}
// [END iot_http_jwt]

// Publish numMessages message asynchronously, starting from message
// messageCount. Telemetry events are published at a rate of 1 per second and
// states at a rate of 1 every 2 seconds.
// [START iot_http_publish]
function publishAsync (authToken, messageCount, numMessages) {
  const payload = `${argv.registryId}/${argv.deviceId}-payload-${messageCount}`;
  console.log('Publishing message:', payload);
  const binaryData = Buffer.from(payload).toString('base64');
  const postData = argv.messageType === 'events' ? {
    binary_data: binaryData
  } : {
    state: {
      binary_data: binaryData
    }
  };

  const options = {
    url: url,
    headers: {
      'authorization': `Bearer ${authToken}`,
      'content-type': 'application/json',
      'cache-control': 'no-cache'
    },
    body: postData,
    json: true,
    method: 'POST',
    retries: 5,
    shouldRetryFn:
      function (incomingHttpMessage) {
        return incomingHttpMessage.statusMessage !== 'OK';
      }
  };

  // Send events for high-frequency updates, update state only occasionally.
  const delayMs = argv.messageType === 'events' ? 1000 : 2000;
  console.log(JSON.stringify(request));
  request(options, function (error, response, body) {
    if (error) {
      console.error('Received error: ', error);
    } else if (response.body.error) {
      console.error('Received error: ' + JSON.stringify(response.body.error));
    } else {
      console.log('Message sent.');
    }
    if (messageCount < numMessages) {
      // If we have published fewer than numMessage messages, publish payload
      // messageCount + 1.
      setTimeout(function () {
        let secsFromIssue = parseInt(Date.now() / 1000) - iatTime;
        if (secsFromIssue > argv.tokenExpMins * 60) {
          iatTime = parseInt(Date.now() / 1000);
          console.log(`\tRefreshing token after ${secsFromIssue} seconds.`);
          authToken = createJwt(argv.projectId, argv.privateKeyFile, argv.algorithm);
        }

        publishAsync(authToken, messageCount + 1, numMessages);
      }, delayMs);
    }
  });
}
// [END iot_http_publish]

// [START iot_http_getconfig]
function getConfig (authToken, version) {
  console.log(`Getting config from URL: ${urlBase}`);

  const options = {
    url: urlBase + '/config?local_version=' + version,
    headers: {
      'authorization': `Bearer ${authToken}`,
      'content-type': 'application/json',
      'cache-control': 'no-cache'

    },
    json: true,
    retries: 5,
    shouldRetryFn:
      function (incomingHttpMessage) {
        console.log('Retry?');
        return incomingHttpMessage.statusMessage !== 'OK';
      }
  };
  console.log(JSON.stringify(request.RetryStrategies));
  request(options, function (error, response, body) {
    if (error) {
      console.error('Received error: ', error);
    } else if (response.body.error) {
      console.error(`Received error: ${JSON.stringify(response.body.error)}`);
    } else {
      console.log('Received config', JSON.stringify(body));
    }
  });
}
// [END iot_http_getconfig]

// [START iot_run_http]

// Print latest configuration
getConfig(authToken, 0);

// Publish messages.
publishAsync(authToken, 1, argv.numMessages);
// [END iot_run_http]


text:
package com.cinchapi.concourse.automation.developer;

import java.nio.file.Path;
import java.nio.file.Paths;

import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;

import com.cinchapi.concourse.util.FileOps;
import com.google.common.io.Files;

/**
 * Unit tests for {@link ConcourseCodebase}.
 *
 * @author Jeff Nelson
 */
public class ConcourseCodebaseTest {

    private static final String ORIGINAL_USER_DIR = System
            .getProperty("user.dir");

    @Rule
    public final TestWatcher __watcher = new TestWatcher() {

        @Override
        protected void finished(Description description) {
            System.setProperty("user.dir", ORIGINAL_USER_DIR);
            ConcourseCodebase.INSTANCE = null;
            Path cache = Paths.get(System.getProperty("user.home"),
                    ConcourseCodebase.REPO_CACHE_FILE);
            cache.toFile().delete();
        }

    };

    @Test
    public void testCloneFromGithubFromWithinRepo() {
        // Assumes that unit test is running from the concourse repo
        ConcourseCodebase codebase = ConcourseCodebase.get();
        Path codepath = codebase.path();
        Path repopath = Paths.get(System.getProperty("user.dir"));
        Assert.assertTrue(isDirectoryOrSubdirectoryOf(codepath, repopath));
    }

    @Test
    public void testCloneFromGithubFromOutsideRepo() {
        System.setProperty("user.dir", Files.createTempDir().toString());
        ConcourseCodebase codebase = ConcourseCodebase.get();
        Path codepath = codebase.path();
        Path repopath = Paths.get(System.getProperty("user.dir"));
        Assert.assertFalse(isDirectoryOrSubdirectoryOf(codepath, repopath));
        Path cache = Paths.get(ConcourseCodebase.REPO_CACHE_FILE);
        Assert.assertTrue(cache.toFile().exists());
        Assert.assertTrue(isDirectoryOrSubdirectoryOf(
                Paths.get(
                        FileOps.readLines(cache.toString()).iterator().next()),
                codepath));
        cache.toFile().delete();
    }

    @Test
    public void testCloneFromGithubOutsideRepoCached() {
        System.setProperty("user.dir", Files.createTempDir().toString());
        ConcourseCodebase codebase = ConcourseCodebase.get();
        ConcourseCodebase.INSTANCE = null; // remove the local process cache
        codebase = ConcourseCodebase.get();
        Path codepath = codebase.path();
        Path repopath = Paths.get(System.getProperty("user.dir"));
        Assert.assertFalse(isDirectoryOrSubdirectoryOf(codepath, repopath));
        Path cache = Paths.get(ConcourseCodebase.REPO_CACHE_FILE);
        Assert.assertTrue(cache.toFile().exists());
        Assert.assertTrue(isDirectoryOrSubdirectoryOf(
                Paths.get(
                        FileOps.readLines(cache.toString()).iterator().next()),
                codepath));
        cache.toFile().delete();
    }

    @Test
    public void testCloneFromGithubDetectStaleCache() {
        System.setProperty("user.dir", Files.createTempDir().toString());
        ConcourseCodebase codebase = ConcourseCodebase.get();
        Path path = Paths.get(FileOps
                .readLines(
                        Paths.get(ConcourseCodebase.REPO_CACHE_FILE).toString())
                .iterator().next());
        path.toFile().delete();
        codebase = ConcourseCodebase.get();
        Path codepath = codebase.path();
        Path repopath = Paths.get(System.getProperty("user.dir"));
        Assert.assertFalse(isDirectoryOrSubdirectoryOf(codepath, repopath));
        Path cache = Paths.get(ConcourseCodebase.REPO_CACHE_FILE);
        Assert.assertTrue(cache.toFile().exists());
        Assert.assertTrue(isDirectoryOrSubdirectoryOf(
                Paths.get(
                        FileOps.readLines(cache.toString()).iterator().next()),
                codepath));
        cache.toFile().delete();
    }

    private static boolean isDirectoryOrSubdirectoryOf(Path parent,
            Path child) {
        parent = parent.normalize().toAbsolutePath();
        if(child != null) {
            child = child.normalize().toAbsolutePath();
        }
        if(child == null) {
            return false;
        }
        else if(parent.equals(child)) {
            return true;
        }
        else {
            return isDirectoryOrSubdirectoryOf(parent, child.getParent());
        }
    }

}


text:
package com.angry_glass_studios.notation.models;

import org.junit.runner.RunWith;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.robolectric.annotation.Config;
import org.robolectric.RobolectricTestRunner;

import static org.fest.assertions.api.Assertions.assertThat;

@RunWith(RobolectricTestRunner.class)
public class TimerModelTest {

  @Before
  public void setUp() throws Exception {
  }

  @After
  public void tearDown() throws Exception {
  }

  @Test
  public void testElapsedTime() throws Exception {
    TimerModel model = new TimerModel();
    assertThat(model.getElapsedTime()).isEqualTo(0);
    model.setElapsedTime(1000L);
    assertThat(model.getElapsedTime()).isEqualTo(1000L);
  }

}