You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pivot.apache.org by tv...@apache.org on 2009/03/16 17:36:28 UTC

svn commit: r754936 [5/38] - in /incubator/pivot/tags/v1.0.1: ./ charts-test/ charts-test/src/ charts-test/src/pivot/ charts-test/src/pivot/charts/ charts-test/src/pivot/charts/test/ charts/ charts/lib/ charts/src/ charts/src/pivot/ charts/src/pivot/ch...

Added: incubator/pivot/tags/v1.0.1/core/src/pivot/collections/concurrent/SynchronizedList.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/core/src/pivot/collections/concurrent/SynchronizedList.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/core/src/pivot/collections/concurrent/SynchronizedList.java (added)
+++ incubator/pivot/tags/v1.0.1/core/src/pivot/collections/concurrent/SynchronizedList.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.collections.concurrent;
+
+import java.util.Comparator;
+import pivot.collections.List;
+import pivot.collections.ListListener;
+import pivot.collections.Sequence;
+import pivot.util.ListenerList;
+import pivot.util.concurrent.SynchronizedListenerList;
+
+/**
+ * Synchronized implementation of the {@link List} interface.
+ *
+ * @author gbrown
+ */
+public class SynchronizedList<T> extends SynchronizedCollection<T>
+    implements List<T> {
+    /**
+     * Synchronized list listener list implementation. Proxies events fired
+     * by inner list to listeners of synchronized list.
+     *
+     * @author gbrown
+     */
+    private class SynchronizedListListenerList
+        extends SynchronizedListenerList<ListListener<T>>
+        implements ListListener<T> {
+        public synchronized void itemInserted(List<T> list, int index) {
+            for (ListListener<T> listener : this) {
+                listener.itemInserted(SynchronizedList.this, index);
+            }
+        }
+
+        public synchronized void itemsRemoved(List<T> list, int index, Sequence<T> items) {
+            for (ListListener<T> listener : this) {
+                listener.itemsRemoved(SynchronizedList.this, index, items);
+            }
+        }
+
+        public synchronized void itemUpdated(List<T> list, int index, T previousItem) {
+            for (ListListener<T> listener : this) {
+                listener.itemUpdated(SynchronizedList.this, index, previousItem);
+            }
+        }
+
+        public synchronized void comparatorChanged(List<T> list, Comparator<T> previousComparator) {
+            for (ListListener<T> listener : this) {
+                listener.comparatorChanged(SynchronizedList.this, previousComparator);
+            }
+        }
+    }
+
+    private SynchronizedListListenerList listListeners = new SynchronizedListListenerList();
+
+    public SynchronizedList(List<T> list) {
+        super(list);
+
+        list.getListListeners().add(listListeners);
+    }
+
+    public synchronized int add(T item) {
+        return ((List<T>)collection).add(item);
+    }
+
+    public synchronized void insert(T item, int index) {
+        ((List<T>)collection).insert(item, index);
+    }
+
+    public synchronized T update(int index, T item) {
+        return ((List<T>)collection).update(index, item);
+    }
+
+    public int remove (T item) {
+        return ((List<T>)collection).remove(item);
+    }
+
+    public synchronized Sequence<T> remove(int index, int count) {
+        return ((List<T>)collection).remove(index, count);
+    }
+
+    public synchronized void clear() {
+        ((List<T>)collection).clear();
+    }
+
+    public synchronized T get(int index) {
+        return ((List<T>)collection).get(index);
+    }
+
+    public synchronized int indexOf(T item) {
+        return ((List<T>)collection).indexOf(item);
+    }
+
+    public synchronized int getLength() {
+        return ((List<T>)collection).getLength();
+    }
+
+    public ListenerList<ListListener<T>> getListListeners() {
+        return listListeners;
+    }
+}

Added: incubator/pivot/tags/v1.0.1/core/src/pivot/collections/concurrent/SynchronizedMap.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/core/src/pivot/collections/concurrent/SynchronizedMap.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/core/src/pivot/collections/concurrent/SynchronizedMap.java (added)
+++ incubator/pivot/tags/v1.0.1/core/src/pivot/collections/concurrent/SynchronizedMap.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.collections.concurrent;
+
+import java.util.Comparator;
+
+import pivot.collections.Map;
+import pivot.collections.MapListener;
+import pivot.util.ListenerList;
+import pivot.util.concurrent.SynchronizedListenerList;
+
+/**
+ * Synchronized implementation of the {@link Map} interface.
+ *
+ * @author gbrown
+ */
+public class SynchronizedMap<K, V> extends SynchronizedCollection<K>
+    implements Map<K, V> {
+    /**
+     * Synchronized map listener list implementation. Proxies events fired
+     * by inner map to listeners of synchronized map.
+     *
+     * @author gbrown
+     */
+    private class SynchronizedMapListenerList
+        extends SynchronizedListenerList<MapListener<K, V>>
+        implements MapListener<K, V> {
+        public synchronized void valueAdded(Map<K, V> map, K key) {
+            for (MapListener<K, V> listener : this) {
+                listener.valueAdded(SynchronizedMap.this, key);
+            }
+        }
+
+        public synchronized void valueRemoved(Map<K, V> map, K key, V value) {
+            for (MapListener<K, V> listener : this) {
+                listener.valueRemoved(SynchronizedMap.this, key, value);
+            }
+        }
+
+        public synchronized void valueUpdated(Map<K, V> map, K key, V previousValue) {
+            for (MapListener<K, V> listener : this) {
+                listener.valueUpdated(SynchronizedMap.this, key, previousValue);
+            }
+        }
+
+        public synchronized void mapCleared(Map<K, V> map) {
+            for (MapListener<K, V> listener : this) {
+                listener.mapCleared(SynchronizedMap.this);
+            }
+        }
+
+        public synchronized void comparatorChanged(Map<K, V> map, Comparator<K> previousComparator) {
+            for (MapListener<K, V> listener : this) {
+                listener.comparatorChanged(SynchronizedMap.this, previousComparator);
+            }
+        }
+    }
+
+    private SynchronizedMapListenerList mapListeners = new SynchronizedMapListenerList();
+
+    public SynchronizedMap(Map<K, V> map) {
+        super(map);
+
+        map.getMapListeners().add(mapListeners);
+    }
+
+    @SuppressWarnings("unchecked")
+    public synchronized V get(K key) {
+        return ((Map<K, V>)collection).get(key);
+    }
+
+    @SuppressWarnings("unchecked")
+    public synchronized V put(K key, V value) {
+        return ((Map<K, V>)collection).put(key, value);
+    }
+
+    @SuppressWarnings("unchecked")
+    public synchronized V remove(K key) {
+        return ((Map<K, V>)collection).remove(key);
+    }
+
+    @SuppressWarnings("unchecked")
+    public synchronized void clear() {
+        ((Map<K, V>)collection).clear();
+    }
+
+    @SuppressWarnings("unchecked")
+    public synchronized boolean isEmpty() {
+        return ((Map<K, V>)collection).isEmpty();
+    }
+
+    @SuppressWarnings("unchecked")
+    public synchronized boolean containsKey(K key) {
+        return ((Map<K, V>)collection).containsKey(key);
+    }
+
+    public ListenerList<MapListener<K, V>> getMapListeners() {
+        return mapListeners;
+    }
+}

Added: incubator/pivot/tags/v1.0.1/core/src/pivot/collections/concurrent/SynchronizedQueue.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/core/src/pivot/collections/concurrent/SynchronizedQueue.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/core/src/pivot/collections/concurrent/SynchronizedQueue.java (added)
+++ incubator/pivot/tags/v1.0.1/core/src/pivot/collections/concurrent/SynchronizedQueue.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.collections.concurrent;
+
+import pivot.collections.Queue;
+
+/**
+ * Synchronized implementation of the {@link Queue} interface.
+ *
+ * @author gbrown
+ */
+public class SynchronizedQueue<T> extends SynchronizedList<T>
+    implements Queue<T> {
+    public SynchronizedQueue(Queue<T> queue) {
+        super(queue);
+    }
+
+    public synchronized void enqueue(T item) {
+        ((Queue<T>)collection).enqueue(item);
+
+        notify();
+    }
+
+    /**
+     * Removes an item from the head of the queue, blocking if the queue is
+     * empty.
+     *
+     * @return
+     * The item at the head of the queue, or null if the removing thread
+     * was interrupted.
+     */
+    public synchronized T dequeue() {
+        T item = null;
+
+        try {
+            while (getLength() == 0) {
+                wait();
+            }
+
+            item = ((Queue<T>)collection).dequeue();
+        } catch(InterruptedException exception) {
+        }
+
+        return item;
+    }
+
+    public synchronized T peek() {
+        return ((Queue<T>)collection).peek();
+    }
+
+    @Override
+    public synchronized int add(T item) {
+        int index = super.add(item);
+
+        notify();
+
+        return index;
+    }
+
+    @Override
+    public synchronized void insert(T item, int index) {
+        super.insert(item, index);
+
+        notify();
+    }
+}

Added: incubator/pivot/tags/v1.0.1/core/src/pivot/collections/concurrent/SynchronizedSet.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/core/src/pivot/collections/concurrent/SynchronizedSet.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/core/src/pivot/collections/concurrent/SynchronizedSet.java (added)
+++ incubator/pivot/tags/v1.0.1/core/src/pivot/collections/concurrent/SynchronizedSet.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.collections.concurrent;
+
+import java.util.Comparator;
+
+import pivot.collections.Set;
+import pivot.collections.SetListener;
+import pivot.util.ListenerList;
+import pivot.util.concurrent.SynchronizedListenerList;
+
+/**
+ * Synchronized implementation of the {@link Set} interface.
+ *
+ * @author gbrown
+ */
+public class SynchronizedSet<E> extends SynchronizedCollection<E>
+    implements Set<E> {
+    /**
+     * Synchronized set listener list implementation. Proxies events fired
+     * by inner set to listeners of synchronized set.
+     *
+     * @author gbrown
+     */
+    private class SynchronizedSetListenerList
+        extends SynchronizedListenerList<SetListener<E>>
+        implements SetListener<E> {
+        public void elementAdded(Set<E> set, E element) {
+            for (SetListener<E> listener : this) {
+                listener.elementAdded(set, element);
+            }
+        }
+
+        public void elementRemoved(Set<E> set, E element) {
+            for (SetListener<E> listener : this) {
+                listener.elementRemoved(SynchronizedSet.this, element);
+            }
+        }
+
+        public void setCleared(Set<E> set) {
+            for (SetListener<E> listener : this) {
+                listener.setCleared(SynchronizedSet.this);
+            }
+        }
+
+        public void comparatorChanged(Set<E> set, Comparator<E> previousComparator) {
+            for (SetListener<E> listener : this) {
+                listener.comparatorChanged(SynchronizedSet.this, previousComparator);
+            }
+        }
+    }
+
+    private SynchronizedSetListenerList setListeners = new SynchronizedSetListenerList();
+
+    public SynchronizedSet(Set<E> set) {
+        super(set);
+
+        set.getSetListeners().add(setListeners);
+    }
+
+    public synchronized void add(E element) {
+        ((Set<E>)collection).add(element);
+    }
+
+    public synchronized void remove(E element) {
+        ((Set<E>)collection).remove(element);
+    }
+
+    public synchronized void clear() {
+        ((Set<E>)collection).clear();
+    }
+
+    public synchronized boolean contains(E element) {
+        return ((Set<E>)collection).contains(element);
+    }
+
+    public synchronized boolean isEmpty() {
+        return ((Set<E>)collection).isEmpty();
+    }
+
+    public ListenerList<SetListener<E>> getSetListeners() {
+        return setListeners;
+    }
+}

Added: incubator/pivot/tags/v1.0.1/core/src/pivot/collections/concurrent/SynchronizedStack.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/core/src/pivot/collections/concurrent/SynchronizedStack.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/core/src/pivot/collections/concurrent/SynchronizedStack.java (added)
+++ incubator/pivot/tags/v1.0.1/core/src/pivot/collections/concurrent/SynchronizedStack.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.collections.concurrent;
+
+import pivot.collections.Stack;
+
+/**
+ * Synchronized implementation of the {@link Stack} interface.
+ *
+ * @author gbrown
+ */
+public class SynchronizedStack<T> extends SynchronizedList<T>
+    implements Stack<T> {
+    public SynchronizedStack(Stack<T> stack) {
+        super(stack);
+    }
+
+    public void push(T item) {
+        ((Stack<T>)collection).push(item);
+    }
+
+    /**
+     * Removes an item from the top of the stack, blocking if the stack is
+     * currently empty.
+     *
+     * @return
+     * The item at the top of the stack, or null if the removing thread
+     * was interrupted.
+     */
+    public T pop() {
+        T item = null;
+
+        try {
+            while (getLength() == 0) {
+                wait();
+            }
+
+            item = ((Stack<T>)collection).pop();
+        }
+        catch(InterruptedException exception) {
+        }
+
+        return item;
+    }
+
+    public T peek() {
+        return ((Stack<T>)collection).peek();
+    }
+
+    public T poke(T item) {
+        return ((Stack<T>)collection).poke(item);
+    }
+}

Added: incubator/pivot/tags/v1.0.1/core/src/pivot/collections/concurrent/package.html
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/core/src/pivot/collections/concurrent/package.html?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/core/src/pivot/collections/concurrent/package.html (added)
+++ incubator/pivot/tags/v1.0.1/core/src/pivot/collections/concurrent/package.html Mon Mar 16 16:36:10 2009
@@ -0,0 +1,6 @@
+<html>
+<head></head>
+<body>
+<p>Contains a set of thread-safe collection implementations.</p>
+</body>
+</html>

Added: incubator/pivot/tags/v1.0.1/core/src/pivot/collections/immutable/ImmutableList.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/core/src/pivot/collections/immutable/ImmutableList.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/core/src/pivot/collections/immutable/ImmutableList.java (added)
+++ incubator/pivot/tags/v1.0.1/core/src/pivot/collections/immutable/ImmutableList.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.collections.immutable;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import pivot.collections.List;
+import pivot.collections.ListListener;
+import pivot.collections.Sequence;
+import pivot.util.ImmutableIterator;
+import pivot.util.ListenerList;
+
+/**
+ * Unmodifiable implementation of the {@link List} interface.
+ *
+ * @author gbrown
+ */
+public final class ImmutableList<T> implements List<T> {
+    private List<T> list = null;
+
+    public ImmutableList(List<T> list) {
+        if (list == null) {
+            throw new IllegalArgumentException("list is null.");
+        }
+
+        this.list = list;
+    }
+
+    public int add(T item) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void insert(T item, int index) {
+        throw new UnsupportedOperationException();
+    }
+
+    public T update(int index, T item) {
+        throw new UnsupportedOperationException();
+    }
+
+    public int remove(T item) {
+        throw new UnsupportedOperationException();
+    }
+
+    public Sequence<T> remove(int index, int count) {
+        throw new UnsupportedOperationException();    }
+
+    public void clear() {
+        throw new UnsupportedOperationException();
+    }
+
+    public T get(int index) {
+        return list.get(index);
+    }
+
+    public int indexOf(T item) {
+        return list.indexOf(item);
+    }
+
+    public int getLength() {
+        return list.getLength();
+    }
+
+    public Comparator<T> getComparator() {
+        return null;
+    }
+
+    public void setComparator(Comparator<T> comparator) {
+        throw new UnsupportedOperationException();
+    }
+
+    public Iterator<T> iterator() {
+        return new ImmutableIterator<T>(list.iterator());
+    }
+
+    public ListenerList<ListListener<T>> getListListeners() {
+        throw new UnsupportedOperationException();
+    }
+}

Added: incubator/pivot/tags/v1.0.1/core/src/pivot/collections/immutable/ImmutableMap.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/core/src/pivot/collections/immutable/ImmutableMap.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/core/src/pivot/collections/immutable/ImmutableMap.java (added)
+++ incubator/pivot/tags/v1.0.1/core/src/pivot/collections/immutable/ImmutableMap.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.collections.immutable;
+
+import java.util.Comparator;
+import java.util.Iterator;
+
+import pivot.collections.Map;
+import pivot.collections.MapListener;
+import pivot.util.ImmutableIterator;
+import pivot.util.ListenerList;
+
+/**
+ * Unmodifiable implementation of the {@link Map} interface.
+ *
+ * @author gbrown
+ */
+public class ImmutableMap<K, V> implements Map<K, V> {
+    private Map<K, V> map = null;
+
+    public ImmutableMap(Map<K, V> map) {
+        if (map == null) {
+            throw new IllegalArgumentException("map is null.");
+        }
+
+        this.map = map;
+    }
+
+    public V get(K key) {
+        return map.get(key);
+    }
+
+    public V put(K key, V value) {
+        throw new UnsupportedOperationException();
+    }
+
+    public V remove(K key) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void clear() {
+        throw new UnsupportedOperationException();
+    }
+
+    public boolean containsKey(K key) {
+        return map.containsKey(key);
+    }
+
+
+    public boolean isEmpty() {
+        return map.isEmpty();
+    }
+
+    public Comparator<K> getComparator() {
+        return null;
+    }
+
+    public void setComparator(Comparator<K> comparator) {
+        throw new UnsupportedOperationException();
+    }
+
+    public Iterator<K> iterator() {
+        return new ImmutableIterator<K>(map.iterator());
+    }
+
+    public ListenerList<MapListener<K, V>> getMapListeners() {
+        throw new UnsupportedOperationException();
+    }
+}

Added: incubator/pivot/tags/v1.0.1/core/src/pivot/collections/immutable/ImmutableSet.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/core/src/pivot/collections/immutable/ImmutableSet.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/core/src/pivot/collections/immutable/ImmutableSet.java (added)
+++ incubator/pivot/tags/v1.0.1/core/src/pivot/collections/immutable/ImmutableSet.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.collections.immutable;
+
+import java.util.Comparator;
+import java.util.Iterator;
+
+import pivot.collections.Set;
+import pivot.collections.SetListener;
+import pivot.util.ImmutableIterator;
+import pivot.util.ListenerList;
+
+/**
+ * Unmodifiable implementation of the {@link Set} interface.
+ *
+ * @author gbrown
+ */
+public class ImmutableSet<E> implements Set<E> {
+    private Set<E> set = null;
+
+    public ImmutableSet(Set<E> set) {
+        if (set == null) {
+            throw new IllegalArgumentException("set is null.");
+        }
+
+        this.set = set;
+    }
+
+    public void add(E element) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void remove(E element) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void clear() {
+        throw new UnsupportedOperationException();
+    }
+
+    public boolean contains(E element) {
+        return set.contains(element);
+    }
+
+    public boolean isEmpty() {
+        return set.isEmpty();
+    }
+
+    public Comparator<E> getComparator() {
+        return null;
+    }
+
+    public void setComparator(Comparator<E> comparator) {
+        throw new UnsupportedOperationException();
+    }
+
+    public Iterator<E> iterator() {
+        return new ImmutableIterator<E>(set.iterator());
+    }
+
+    public ListenerList<SetListener<E>> getSetListeners() {
+        throw new UnsupportedOperationException();
+    }
+}

Added: incubator/pivot/tags/v1.0.1/core/src/pivot/collections/immutable/package.html
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/core/src/pivot/collections/immutable/package.html?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/core/src/pivot/collections/immutable/package.html (added)
+++ incubator/pivot/tags/v1.0.1/core/src/pivot/collections/immutable/package.html Mon Mar 16 16:36:10 2009
@@ -0,0 +1,6 @@
+<html>
+<head></head>
+<body>
+<p>Contains a set of read-only collection implementations.</p>
+</body>
+</html>

Added: incubator/pivot/tags/v1.0.1/core/src/pivot/collections/package.html
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/core/src/pivot/collections/package.html?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/core/src/pivot/collections/package.html (added)
+++ incubator/pivot/tags/v1.0.1/core/src/pivot/collections/package.html Mon Mar 16 16:36:10 2009
@@ -0,0 +1,6 @@
+<html>
+<head></head>
+<body>
+<p>Defines a set of classes and interfaces that serve as generic collections as well as the data model for UI components.</p>
+</body>
+</html>

Added: incubator/pivot/tags/v1.0.1/core/src/pivot/serialization/BinarySerializer.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/core/src/pivot/serialization/BinarySerializer.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/core/src/pivot/serialization/BinarySerializer.java (added)
+++ incubator/pivot/tags/v1.0.1/core/src/pivot/serialization/BinarySerializer.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.serialization;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+
+/**
+ * Implementation of the {@link Serializer} interface that uses Java's
+ * internal serialization mechanism to read and write values. All values in the
+ * object hierarchy are required to implement {@link Serializable}.
+ *
+ * @author gbrown
+ */
+public class BinarySerializer implements Serializer {
+    public static final String MIME_TYPE = "application/x-java-serialized-object";
+
+    /**
+     * Reads a sequence of serialized objects from an input stream.
+     */
+    public Object readObject(InputStream inputStream) throws IOException,
+        SerializationException {
+        if (inputStream == null) {
+            throw new IllegalArgumentException("inputStream is null.");
+        }
+
+        Object object = null;
+
+        try {
+            ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
+            object = objectInputStream.readObject();
+        } catch(ClassNotFoundException exception) {
+            throw new SerializationException(exception);
+        }
+
+        return object;
+    }
+
+    /**
+     * Writes a sequence of serializable objects to an output stream.
+     */
+    public void writeObject(Object object, OutputStream outputStream)
+        throws IOException, SerializationException {
+        if (object == null) {
+            throw new IllegalArgumentException("object is null.");
+        }
+
+        if (outputStream == null) {
+            throw new IllegalArgumentException("outputStream is null.");
+        }
+
+        ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
+        objectOutputStream.writeObject(object);
+    }
+
+    public String getMIMEType() {
+        return MIME_TYPE;
+    }
+}

Added: incubator/pivot/tags/v1.0.1/core/src/pivot/serialization/CSVSerializer.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/core/src/pivot/serialization/CSVSerializer.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/core/src/pivot/serialization/CSVSerializer.java (added)
+++ incubator/pivot/tags/v1.0.1/core/src/pivot/serialization/CSVSerializer.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.serialization;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.Writer;
+
+import pivot.collections.ArrayList;
+import pivot.collections.Dictionary;
+import pivot.collections.HashMap;
+import pivot.collections.List;
+import pivot.collections.Sequence;
+
+/**
+ * Implementation of the {@link Serializer} interface that reads data from
+ * and writes data to a comma-separated value (CSV) file.
+ * <p>
+ * TODO Allow caller to specify a class that does not implement Dictionary.
+ * We can use BeanDictionary to allow the caller to instantiate and populate
+ * arbitrary types.
+ * <p>
+ * TODO Add "firstLineContainsKeys" flag.
+ * <p>
+ * TODO Add support for variable delimiters.
+ *
+ * @author gbrown
+ */
+public class CSVSerializer implements Serializer {
+    public static final String MIME_TYPE = "text/csv";
+
+    /**
+     * Class representing the serializers key sequence.
+     */
+    public class KeySequence implements Sequence<String> {
+        public int add(String item) {
+            return keys.add(item);
+        }
+
+        public void insert(String item, int index) {
+            keys.insert(item, index);
+        }
+
+        public String update(int index, String item) {
+            return keys.update(index, item);
+        }
+
+        public int remove(String item) {
+            return keys.remove(item);
+        }
+
+        public Sequence<String> remove(int index, int count) {
+            return keys.remove(index, count);
+        }
+
+        public String get(int index) {
+            return keys.get(index);
+        }
+
+        public int indexOf(String item) {
+            return keys.indexOf(item);
+        }
+
+        public int getLength() {
+            return keys.getLength();
+        }
+    }
+
+    private ArrayList<String> keys = new ArrayList<String>();
+    private KeySequence keySequence = new KeySequence();
+
+    int c = -1;
+    private Class<?> itemClass = HashMap.class;
+
+    public CSVSerializer() {
+    }
+
+    /**
+     * Returns a sequence representing the fields that will be read or written
+     * by this serializer.
+     */
+    public KeySequence getKeys() {
+        return keySequence;
+    }
+
+    /**
+     * Returns the item class that will be instantiated by the serializer during
+     * a read operation.
+     */
+    public Class<?> getItemClass() {
+        return itemClass;
+    }
+
+    /**
+     * Sets the item class that will be instantiated by the serializer during
+     * a read operation. The class must implement the {@link Dictionary}
+     * interface.
+     */
+    public void setItemClass(Class<?> itemClass) {
+        if (itemClass == null) {
+            throw new IllegalArgumentException("itemClass is null.");
+        }
+
+        this.itemClass = itemClass;
+    }
+
+    /**
+     * Reads values from a comma-separated value stream.
+     *
+     * @param inputStream
+     * The input stream from which data will be read.
+     *
+     * @see #readObject(Reader)
+     */
+    @SuppressWarnings("unchecked")
+    public Object readObject(InputStream inputStream)
+        throws IOException, SerializationException {
+        Reader reader = new InputStreamReader(inputStream);
+        Object object = readObject(reader);
+
+        return object;
+    }
+
+    /**
+     * Reads values from a comma-separated value stream.
+     *
+     * @param reader
+     * The reader from which data will be read.
+     *
+     * @return
+     * An instance of List<Object> containing the data read from the CSV file.
+     * The list items are instances of Dictionary<String, Object> populated by
+     * mapping columns in the CSV file to keys in the key sequence.
+     */
+    public Object readObject(Reader reader)
+        throws IOException, SerializationException {
+        ArrayList<Dictionary<String, Object>> items = new ArrayList<Dictionary<String, Object>>();
+
+        // Move to the first character
+        c = reader.read();
+
+        while (c != -1) {
+            Dictionary<String, Object> item = readItem(reader);
+            while (item != null) {
+                items.add(item);
+
+                // Move to next line
+                while (c != -1
+                    && (c == '\r' || c == '\n')) {
+                    c = reader.read();
+                }
+
+                // Read the next item
+                item = readItem(reader);
+            }
+        }
+
+        return items;
+    }
+
+    @SuppressWarnings("unchecked")
+    private Dictionary<String, Object> readItem(Reader reader)
+        throws IOException, SerializationException {
+        Dictionary<String, Object> item = null;
+
+        if (c != -1) {
+            // Instantiate the item
+            try {
+                item = (Dictionary<String, Object>)itemClass.newInstance();
+            } catch(IllegalAccessException exception) {
+                throw new SerializationException(exception);
+            } catch(InstantiationException exception) {
+                throw new SerializationException(exception);
+            }
+
+            // Add values to the item
+            for (int i = 0, n = keys.getLength(); i < n; i++) {
+                String key = keys.get(i);
+                String value = readValue(reader);
+                if (value == null) {
+                    throw new SerializationException("Error reading value for "
+                        + key + " from input stream.");
+                }
+
+                item.put(key, value);
+            }
+        }
+
+        return item;
+    }
+
+    private String readValue(Reader reader)
+        throws IOException, SerializationException {
+        String value = null;
+
+        // Read the next value from this line, returning null if there are
+        // no more values on the line
+        if (c != -1
+            && (c != '\r' && c != '\n')) {
+            // Read the value
+            StringBuilder valueBuilder = new StringBuilder();
+
+            // Values may be bounded in quotes; the double-quote character is
+            // escaped by two successive occurrences
+            boolean quoted = (c == '"');
+            if (quoted) {
+                c = reader.read();
+            }
+
+            while (c != -1
+                && (quoted || c != ',')
+                && (c != '\r' && c != '\n')) {
+                if (c == '"') {
+                    c = reader.read();
+                    quoted &= (c == '"');
+                }
+
+                if (quoted || c != ',') {
+                    valueBuilder.append((char)c);
+                    c = reader.read();
+                }
+            }
+
+            if (quoted) {
+                throw new SerializationException("Unterminated string.");
+            }
+
+            value = valueBuilder.toString();
+
+            // Move to the next character after ','
+            c = reader.read();
+        }
+
+        return value;
+    }
+
+    /**
+     * Writes values to a comma-separated value stream.
+     *
+     * @param object
+     *
+     * @param outputStream
+     * The output stream to which data will be written.
+     *
+     * @see #writeObject(Object, Writer)
+     */
+    public void writeObject(Object object, OutputStream outputStream)
+        throws IOException, SerializationException {
+        Writer writer = null;
+
+        try {
+            writer = new OutputStreamWriter(outputStream);
+            writeObject(object, writer);
+        } finally {
+            if (writer != null) {
+                writer.flush();
+            }
+        }
+    }
+
+    /**
+     * Writes values to a comma-separated value stream.
+     *
+     * @param object
+     * An instance of List<Object> containing the data to write to the CSV
+     * file. List items must be instances of Dictionary<String, Object>. The
+     * dictionary values will be written out in the order specified by the
+     * key sequence.
+     *
+     * @param writer
+     * The writer to which data will be written.
+     */
+    @SuppressWarnings("unchecked")
+    public void writeObject(Object object, Writer writer)
+        throws IOException, SerializationException {
+        List<Dictionary<String, Object>> items = (List<Dictionary<String, Object>>)object;
+
+        PrintWriter printWriter = null;
+
+        try {
+            printWriter = new PrintWriter(writer);
+
+            for (Dictionary<String, Object> item : items) {
+                for (int i = 0, n = keys.getLength(); i < n; i++) {
+                    String key = keys.get(i);
+
+                    if (i > 0) {
+                        printWriter.print(",");
+                    }
+
+                    Object value = item.get(key);
+                    printWriter.print(value.toString());
+                }
+
+                printWriter.println();
+            }
+        } finally {
+            if (printWriter != null) {
+                printWriter.close();
+            }
+        }
+    }
+
+    public String getMIMEType() {
+        return MIME_TYPE;
+    }
+}

Added: incubator/pivot/tags/v1.0.1/core/src/pivot/serialization/JSONSerializer.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/core/src/pivot/serialization/JSONSerializer.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/core/src/pivot/serialization/JSONSerializer.java (added)
+++ incubator/pivot/tags/v1.0.1/core/src/pivot/serialization/JSONSerializer.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,824 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.serialization;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.Writer;
+import java.nio.charset.Charset;
+
+import pivot.collections.ArrayList;
+import pivot.collections.Dictionary;
+import pivot.collections.HashMap;
+import pivot.collections.List;
+import pivot.collections.Map;
+import pivot.collections.Sequence;
+
+/**
+ * Implementation of the {@link Serializer} interface that reads data from
+ * and writes data to a JavaScript Object Notation (JSON) file.
+ * <p>
+ * TODO Wrap reader in a CountingReader that tracks line/character index.
+ *
+ * @author gbrown
+ */
+public class JSONSerializer implements Serializer {
+    private Charset charset = null;
+
+    private int c = -1;
+    private boolean alwaysDelimitMapKeys = false;
+
+    public static final String MIME_TYPE = "application/json";
+
+    public JSONSerializer() {
+        this(Charset.defaultCharset());
+    }
+
+    public JSONSerializer(String charsetName) {
+        this(Charset.forName(charsetName));
+    }
+
+    public JSONSerializer(Charset charset) {
+        if (charset == null) {
+            throw new IllegalArgumentException("charset is null.");
+        }
+
+        this.charset = charset;
+    }
+
+    /**
+     * Reads data from a JSON stream.
+     *
+     * @param inputStream
+     * The input stream from which data will be read.
+     *
+     * @see #readObject(Reader)
+     */
+    public Object readObject(InputStream inputStream)
+        throws IOException, SerializationException {
+        Reader reader = new InputStreamReader(inputStream, charset);
+        Object object = readObject(reader);
+
+        return object;
+    }
+
+    /**
+     * Reads data from a JSON stream.
+     *
+     * @param reader
+     * The reader from which data will be read.
+     *
+     * @return
+     * One of the following types, depending on the content of the stream:
+     *
+     * <ul>
+     * <li>java.lang.String</li>
+     * <li>java.lang.Number</li>
+     * <li>java.lang.Boolean</li>
+     * <li>pivot.collections.List</li>
+     * <li>pivot.collections.Map</li>
+     * </ul>
+     */
+    public Object readObject(Reader reader)
+        throws IOException, SerializationException {
+        // Move to the first character
+        c = reader.read();
+
+        // Read the root value
+        Object object = readValue(reader);
+
+        return object;
+    }
+
+    private Object readValue(Reader reader)
+        throws IOException, SerializationException {
+        Object object = null;
+
+        skipWhitespace(reader);
+
+        if (c == -1) {
+            throw new SerializationException("Unexpected end of input stream.");
+        }
+
+        if (c == 'n') {
+            object = readNull(reader);
+        } else if (c == '"' || c == '\'') {
+            object = readString(reader);
+        } else if (c == '+' || c == '-' || Character.isDigit(c)) {
+            object = readNumber(reader);
+        } else if (c == 't' || c == 'f') {
+            object = readBoolean(reader);
+        } else if (c == '[') {
+            object = readList(reader);
+        } else if (c == '{') {
+            object = readMap(reader);
+        } else {
+            throw new SerializationException("Unexpected character in input stream.");
+        }
+
+        return object;
+    }
+
+    private void skipWhitespace(Reader reader)
+        throws IOException {
+        while (c != -1 && Character.isWhitespace(c)) {
+            c = reader.read();
+        }
+    }
+
+    private Object readNull(Reader reader)
+        throws IOException, SerializationException {
+        String nullString = "null";
+
+        int n = nullString.length();
+        int i = 0;
+
+        while (c != -1 && i < n) {
+            if (nullString.charAt(i) != c) {
+                throw new SerializationException("Unexpected character in input stream.");
+            }
+
+            c = reader.read();
+            i++;
+        }
+
+        if (i < n) {
+            throw new SerializationException("Incomplete null value in input stream.");
+        }
+
+        return null;
+    }
+
+    private String readString(Reader reader)
+        throws IOException, SerializationException {
+        StringBuilder stringBuilder = new StringBuilder();
+
+        // Use the same delimiter to close the string
+        int t = c;
+
+        // Move to the next character after the delimiter
+        c = reader.read();
+
+        while (c != -1 && c != t) {
+            if (c == '\\') {
+                c = reader.read();
+                if (!(c == '\'' || c == t)) {
+                    throw new SerializationException("Unsupported escape sequence in input stream.");
+                }
+            }
+
+            stringBuilder.append((char)c);
+            c = reader.read();
+        }
+
+        if (c != t) {
+            throw new SerializationException("Unterminated string in input stream.");
+        }
+
+        // Move to the next character after the delimiter
+        c = reader.read();
+
+        return stringBuilder.toString();
+    }
+
+    private Number readNumber(Reader reader)
+        throws IOException, SerializationException {
+        Number number = null;
+
+        StringBuilder stringBuilder = new StringBuilder();
+        boolean negative = false;
+        boolean integer = true;
+
+        if (c == '+' || c == '-') {
+            negative = (c == '-');
+            c = reader.read();
+        }
+
+        while (c != -1 && (Character.isDigit(c) || c == '.'
+            || c == 'e' || c == 'E')) {
+            stringBuilder.append((char)c);
+            integer &= !(c == '.');
+            c = reader.read();
+        }
+
+        if (integer) {
+            // TODO 5/28/2008 Remove 32-bit optimization when 64-bit processors
+            // are more prevalent
+            long value = Long.parseLong(stringBuilder.toString()) * (negative ? -1 : 1);
+
+            if (value > Integer.MAX_VALUE
+                || value < Integer.MIN_VALUE) {
+                number = value;
+            } else {
+                number = (int)value;
+            }
+        } else {
+            number = Double.parseDouble(stringBuilder.toString()) * (negative ? -1.0d : 1.0d);
+        }
+
+        return number;
+    }
+
+    private Boolean readBoolean(Reader reader)
+        throws IOException, SerializationException {
+        String booleanString = (c == 't') ? "true" : "false";
+        int n = booleanString.length();
+        int i = 0;
+
+        while (c != -1 && i < n) {
+            if (booleanString.charAt(i) != c) {
+                throw new SerializationException("Unexpected character in input stream.");
+            }
+
+            c = reader.read();
+            i++;
+        }
+
+        if (i < n) {
+            throw new SerializationException("Incomplete boolean value in input stream.");
+        }
+
+        return Boolean.parseBoolean(booleanString);
+    }
+
+    private List<Object> readList(Reader reader)
+        throws IOException, SerializationException {
+        List<Object> list = new ArrayList<Object>();
+
+        // Move to the next character after '['
+        c = reader.read();
+
+        while (c != -1 && c != ']') {
+            list.add(readValue(reader));
+            skipWhitespace(reader);
+
+            if (c == ',') {
+                c = reader.read();
+                skipWhitespace(reader);
+            } else if (c == -1) {
+                throw new SerializationException("Unexpected end of input stream.");
+            } else {
+                if (c != ']') {
+                    throw new SerializationException("Unexpected character in input stream.");
+                }
+            }
+        }
+
+        // Move to the next character after ']'
+        c = reader.read();
+
+        return list;
+    }
+
+    private Map<String, Object> readMap(Reader reader)
+        throws IOException, SerializationException {
+        Map<String, Object> map = new HashMap<String, Object>();
+
+        // Move to the next character after '{'
+        c = reader.read();
+        skipWhitespace(reader);
+
+        while (c != -1 && c != '}') {
+            String key = null;
+
+            if (c == '"' || c == '\'') {
+                // The key is a delimited string
+                key = readString(reader);
+            } else {
+                // The key is an undelimited string; it must adhere to Java
+                // identifier syntax
+                StringBuilder keyStringBuilder = new StringBuilder();
+
+                if (!Character.isJavaIdentifierStart(c)) {
+                    throw new SerializationException("Illegal identifier start character.");
+                }
+
+                while (c != -1
+                    && c != ':' && !Character.isWhitespace(c)) {
+                    if (!Character.isJavaIdentifierPart(c)) {
+                        throw new SerializationException("Illegal identifier character.");
+                    }
+
+                    keyStringBuilder.append((char)c);
+                    c = reader.read();
+                }
+
+                if (c == -1) {
+                    throw new SerializationException("Unexpected end of input stream.");
+                }
+
+                key = keyStringBuilder.toString();
+            }
+
+            if (key == null
+                || key.length() == 0) {
+                throw new SerializationException("\"" + key + "\" is not a valid key.");
+            }
+
+            skipWhitespace(reader);
+
+            if (c != ':') {
+                throw new SerializationException("Unexpected character in input stream.");
+            }
+
+            // Move to the first character after ':'
+            c = reader.read();
+
+            map.put(key, readValue(reader));
+            skipWhitespace(reader);
+
+            if (c == ',') {
+                c = reader.read();
+                skipWhitespace(reader);
+            } else if (c == -1) {
+                throw new SerializationException("Unexpected end of input stream.");
+            } else {
+                if (c != '}') {
+                    throw new SerializationException("Unexpected character in input stream.");
+                }
+            }
+        }
+
+        // Move to the first character after '}'
+        c = reader.read();
+
+        return map;
+    }
+
+    /**
+     * Writes data to a JSON stream.
+     *
+     * @param object
+     *
+     * @param outputStream
+     * The output stream to which data will be written.
+     *
+     * @see #writeObject(Object, Writer)
+     */
+    public void writeObject(Object object, OutputStream outputStream)
+        throws IOException, SerializationException {
+        Writer writer = null;
+
+        try {
+            writer = new OutputStreamWriter(outputStream, charset);
+            writeObject(object, writer);
+        } finally {
+            if (writer != null) {
+                writer.flush();
+            }
+        }
+    }
+
+    /**
+     * Writes data to a JSON stream.
+     *
+     * @param object
+     * The object to serialize. Must be one of the following types:
+     *
+     * <ul>
+     * <li>java.lang.String</li>
+     * <li>java.lang.Number</li>
+     * <li>java.lang.Boolean</li>
+     * <li>pivot.collections.List</li>
+     * <li>pivot.collections.Map</li>
+     * </ul>
+     *
+     * @param writer
+     * The writer to which data will be written.
+     */
+    @SuppressWarnings("unchecked")
+    public void writeObject(Object object, Writer writer)
+        throws IOException, SerializationException {
+        if (object == null) {
+            writer.append("null");
+        } else if (object instanceof String) {
+            writer.append("\"" + ((String)object).replace("\"", "\\\"") + "\"");
+        } else if (object instanceof Number) {
+            writer.append(object.toString());
+        } else if (object instanceof Boolean) {
+            writer.append(object.toString());
+        } else if (object instanceof List<?>) {
+            List<Object> list = (List<Object>)object;
+            writer.append("[");
+
+            int i = 0;
+            for (Object item : list) {
+                if (i > 0) {
+                    writer.append(", ");
+                }
+
+                writeObject(item, writer);
+                i++;
+            }
+
+            writer.append("]");
+        } else if (object instanceof Map<?, ?>) {
+            Map<String, Object> map = (Map<String, Object>)object;
+            writer.append("{");
+
+            int i = 0;
+            for (String key : map) {
+                Object value = map.get(key);
+
+                boolean identifier = true;
+                StringBuilder keyStringBuilder = new StringBuilder();
+
+                for (int j = 0, n = key.length(); j < n; j++) {
+                    char c = key.charAt(j);
+                    identifier &= Character.isJavaIdentifierPart(c);
+
+                    if (c == '"') {
+                        keyStringBuilder.append('\\');
+                    }
+
+                    keyStringBuilder.append(c);
+                }
+
+                key = keyStringBuilder.toString();
+
+                if (i > 0) {
+                    writer.append(", ");
+                }
+
+                // Write the key
+                if (!identifier || alwaysDelimitMapKeys) {
+                    writer.append('"');
+                }
+
+                writer.append(key);
+
+                if (!identifier || alwaysDelimitMapKeys) {
+                    writer.append('"');
+                }
+
+                writer.append(": ");
+
+                // Write the value
+                writeObject(value, writer);
+
+                i++;
+            }
+
+            writer.append("}");
+        } else {
+            throw new IllegalArgumentException(object.getClass()
+                + " is not a supported type.");
+        }
+    }
+
+    public String getMIMEType() {
+        return MIME_TYPE;
+    }
+
+    /**
+     * Returns a flag indicating whether or not map keys will always be
+     * quote-delimited.
+     */
+    public boolean getAlwaysDelimitMapKeys() {
+        return alwaysDelimitMapKeys;
+    }
+
+    /**
+     * Sets a flag indicating that map keys should always be quote-delimited.
+     *
+     * @param alwaysDelimitMapKeys
+     * <tt>true</tt> to bound map keys in double quotes; <tt>false</tt> to
+     * only quote-delimit keys as necessary.
+     */
+    public void setAlwaysDelimitMapKeys(boolean alwaysDelimitMapKeys) {
+        this.alwaysDelimitMapKeys = alwaysDelimitMapKeys;
+    }
+
+    /**
+     * Returns the value at the given path.
+     *
+     * @param root
+     * The root object; must be an instance of {@link pivot.collections.Map}
+     * or {@link pivot.collections.List}.
+     *
+     * @param path
+     * The path to the value, in JavaScript path notation.
+     *
+     * @return
+     * The value at the given path.
+     */
+    @SuppressWarnings("unchecked")
+    public static Object getValue(Object root, String path) {
+        Object value = root;
+
+        int i = 0;
+        int n = path.length();
+
+        while (i < n) {
+            char c = path.charAt(i++);
+
+            boolean keyed = true;
+            StringBuilder identifierBuilder = new StringBuilder();
+
+            boolean bracketed = (c == '[');
+            if (bracketed
+                && i < n) {
+                c = path.charAt(i++);
+
+                char quote = Character.UNASSIGNED;
+
+                boolean quoted = (c == '"'
+                    || c == '\'');
+                if (quoted
+                    && i < n) {
+                    quote = c;
+                    c = path.charAt(i++);
+                }
+
+                keyed = quoted;
+
+                while (i <= n
+                    && bracketed) {
+                    bracketed = quoted || (c != ']');
+
+                    if (bracketed) {
+                        if (c == quote) {
+                            if (i < n) {
+                                c = path.charAt(i++);
+                                quoted = (c == quote);
+                            }
+                        }
+
+                        if (quoted || c != ']') {
+                            if (Character.isISOControl(c)) {
+                                throw new IllegalArgumentException("Illegal identifier character.");
+                            }
+
+                            identifierBuilder.append(c);
+
+                            if (i < n) {
+                                c = path.charAt(i++);
+                            }
+                        }
+                    }
+                }
+
+                if (quoted) {
+                    throw new IllegalArgumentException("Unterminated quoted identifier.");
+                }
+
+                if (bracketed) {
+                    throw new IllegalArgumentException("Unterminated bracketed identifier.");
+                }
+
+                if (i < n) {
+                    c = path.charAt(i);
+
+                    if (c == '.') {
+                        i++;
+                    }
+                }
+            } else {
+                keyed = true;
+
+                while(i <= n
+                    && c != '.'
+                    && c != '[') {
+                    if (!Character.isJavaIdentifierPart(c)) {
+                        throw new IllegalArgumentException("Illegal identifier character.");
+                    }
+
+                    identifierBuilder.append(c);
+
+                    if (i < n) {
+                        c = path.charAt(i);
+                    }
+
+                    i++;
+                }
+
+                if (c == '[') {
+                    i--;
+                }
+            }
+
+            if (c == '.'
+                && i == n) {
+                throw new IllegalArgumentException("Path cannot end with a '.' character.");
+            }
+
+            if (identifierBuilder.length() == 0) {
+                throw new IllegalArgumentException("Missing identifier.");
+            }
+
+            String identifier = identifierBuilder.toString();
+
+            if (keyed) {
+                if (!(value instanceof Dictionary<?, ?>)){
+                    throw new IllegalArgumentException("Invalid path.");
+                }
+
+                String key = identifier;
+                Dictionary<String, Object> dictionary = (Dictionary<String, Object>)value;
+                value = dictionary.get(key);
+            } else {
+                if (!(value instanceof Sequence<?>)){
+                    throw new IllegalArgumentException("Invalid path.");
+                }
+
+                int index = Integer.parseInt(identifier);
+                Sequence<Object> sequence = (Sequence<Object>)value;
+                value = sequence.get(index);
+            }
+        }
+
+        return value;
+    }
+
+    /**
+     * Returns the value at the given path as a string.
+     *
+     * @param root
+     * @param path
+     *
+     * @see #getValue(Object, String)
+     */
+    public static String getString(Object root, String path) {
+        return (String)getValue(root, path);
+    }
+
+    /**
+     * Returns the value at the given path as a number.
+     *
+     * @param root
+     * @param path
+     *
+     * @see #getValue(Object, String)
+     */
+    public static Number getNumber(Object root, String path) {
+        return (Number)getValue(root, path);
+    }
+
+    /**
+     * Returns the value at the given path as a short.
+     *
+     * @param root
+     * @param path
+     *
+     * @see #getValue(Object, String)
+     */
+    public static Short getShort(Object root, String path) {
+        return (Short)getValue(root, path);
+    }
+
+    /**
+     * Returns the value at the given path as an integer.
+     *
+     * @param root
+     * @param path
+     *
+     * @see #getValue(Object, String)
+     */
+    public static Integer getInteger(Object root, String path) {
+        return (Integer)getValue(root, path);
+    }
+
+    /**
+     * Returns the value at the given path as a long.
+     *
+     * @param root
+     * @param path
+     *
+     * @see #getValue(Object, String)
+     */
+    public static Long getLong(Object root, String path) {
+        return (Long)getValue(root, path);
+    }
+
+    /**
+     * Returns the value at the given path as a float.
+     *
+     * @param root
+     * @param path
+     *
+     * @see #getValue(Object, String)
+     */
+    public static Float getFloat(Object root, String path) {
+        return (Float)getValue(root, path);
+    }
+
+    /**
+     * Returns the value at the given path as a double.
+     *
+     * @param root
+     * @param path
+     *
+     * @see #getValue(Object, String)
+     */
+    public static Double getDouble(Object root, String path) {
+        return (Double)getValue(root, path);
+    }
+
+    /**
+     * Returns the value at the given path as a boolean.
+     *
+     * @param root
+     * @param path
+     *
+     * @see #getValue(Object, String)
+     */
+    public static Boolean getBoolean(Object root, String path) {
+        return (Boolean)getValue(root, path);
+    }
+
+    /**
+     * Returns the value at the given path as a list.
+     *
+     * @param root
+     * @param path
+     *
+     * @see #getValue(Object, String)
+     */
+    @SuppressWarnings("unchecked")
+    public static List<?> getList(Object root, String path) {
+        return (List<?>)getValue(root, path);
+    }
+
+    /**
+     * Returns the value at the given path as a map.
+     *
+     * @param root
+     * @param path
+     *
+     * @see #getValue(Object, String)
+     */
+    @SuppressWarnings("unchecked")
+    public static Map<String, ?> getMap(Object root, String path) {
+        return (Map<String, ?>)getValue(root, path);
+    }
+
+    /**
+     * Parses a JSON-formatted array value into a list.
+     *
+     * @param string
+     * A string containing a JSON array (e.g. "[1, 2, 3]").
+     *
+     * @return
+     * A {@link List} instance containing the parsed JSON data.
+     */
+    @SuppressWarnings("unchecked")
+    public static List<?> parseList(String string) {
+        List<?> list = null;
+        JSONSerializer jsonSerializer = new JSONSerializer();
+
+        Object object = null;
+        try {
+            object = jsonSerializer.readObject(new StringReader(string));
+        } catch(Exception exception) {
+            throw new RuntimeException(exception);
+        }
+
+        list = (List<?>)object;
+
+        return list;
+    }
+
+    /**
+     * Parses a JSON-formatted object value into a map.
+     *
+     * @param string
+     * A string containing a JSON object (e.g. "{a:1, b:2, c:3}").
+     *
+     * @return
+     * A {@link Map} instance containing the parsed JSON data.
+     */
+    @SuppressWarnings("unchecked")
+    public static Map<String, ?> parseMap(String string) {
+        Map<String, ?> map = null;
+        JSONSerializer jsonSerializer = new JSONSerializer();
+
+        Object object = null;
+        try {
+            object = jsonSerializer.readObject(new StringReader(string));
+        } catch(Exception exception) {
+            throw new RuntimeException(exception);
+        }
+
+        map = (Map<String, ?>)object;
+
+        return map;
+    }
+}

Added: incubator/pivot/tags/v1.0.1/core/src/pivot/serialization/PropertiesSerializer.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/core/src/pivot/serialization/PropertiesSerializer.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/core/src/pivot/serialization/PropertiesSerializer.java (added)
+++ incubator/pivot/tags/v1.0.1/core/src/pivot/serialization/PropertiesSerializer.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.serialization;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Properties;
+
+import pivot.collections.Map;
+import pivot.collections.adapter.MapAdapter;
+
+/**
+ * Implementation of the {@link Serializer} interface that reads data from
+ * and writes data to the Java properties file format.
+ *
+ * @author smartini
+ * @author gbrown
+ */
+public class PropertiesSerializer implements Serializer {
+    public static final String MIME_TYPE = "text/plain";
+
+    /**
+     * Reads data from a properties stream.
+     *
+     * @param inputStream
+     * The input stream from which data will be read.
+     *
+     * @return
+     * An instance of {@link Map} containing the data read from the properties
+     * file. Both keys and values are strings.
+     */
+    @SuppressWarnings("unchecked")
+    public Object readObject(InputStream inputStream) throws IOException,
+        SerializationException {
+        if (inputStream == null) {
+            throw new IllegalArgumentException("inputStream is null.");
+        }
+
+        Properties properties = new Properties();
+        properties.load(inputStream);
+
+        return new MapAdapter<Object, Object>(properties);
+    }
+
+    /**
+     * Writes data to a properties stream.
+     *
+     * @param object
+     * An instance of {@link Map} containing the data to be written to the
+     * properties file. Keys must be strings, and values will be converted to
+     * strings.
+     *
+     * @param outputStream
+     * The output stream to which data will be written.
+     */
+    @SuppressWarnings("unchecked")
+    public void writeObject(Object object, OutputStream outputStream) throws IOException,
+        SerializationException {
+        if (!(object instanceof Map<?, ?>)) {
+            throw new IllegalArgumentException("object must be an instance of "
+                + Map.class.getName());
+        }
+
+        if (outputStream == null) {
+            throw new IllegalArgumentException("outputStream is null.");
+        }
+
+        Map<String, Object> map = (Map<String, Object>) object;
+        Properties properties = new Properties();
+
+        for (String key : map) {
+            Object value = map.get(key);
+            if (value != null) {
+                value = value.toString();
+            }
+
+            properties.put(key, value);
+        }
+
+        properties.store(outputStream, null);
+    }
+
+    public String getMIMEType() {
+        return MIME_TYPE;
+    }
+}

Added: incubator/pivot/tags/v1.0.1/core/src/pivot/serialization/SerializationException.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/core/src/pivot/serialization/SerializationException.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/core/src/pivot/serialization/SerializationException.java (added)
+++ incubator/pivot/tags/v1.0.1/core/src/pivot/serialization/SerializationException.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.serialization;
+
+/**
+ * Thrown when an error is encountered during serialization.
+ *
+ * @author gbrown
+ */
+public class SerializationException extends Exception {
+    public static final long serialVersionUID = 0;
+
+    public SerializationException() {
+        this(null, null);
+    }
+
+    public SerializationException(String message) {
+        this(message, null);
+    }
+
+    public SerializationException(Throwable cause) {
+        this(null, cause);
+    }
+
+    public SerializationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}

Added: incubator/pivot/tags/v1.0.1/core/src/pivot/serialization/Serializer.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/core/src/pivot/serialization/Serializer.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/core/src/pivot/serialization/Serializer.java (added)
+++ incubator/pivot/tags/v1.0.1/core/src/pivot/serialization/Serializer.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.serialization;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+
+/**
+ * Defines an interface for writing objects to and reading objects from a data
+ * stream.
+ *
+ * @author gbrown
+ */
+public interface Serializer {
+    /**
+     * Deserializes an object from an input stream.
+     *
+     * @param inputStream
+     * The data stream from which the object will be read.
+     *
+     * @return
+     * The deserialized object.
+     */
+    public Object readObject(InputStream inputStream) throws IOException, SerializationException;
+
+    /**
+     * Serializes an object to an output stream.
+     *
+     * @param object
+     * The object to serialize.
+     *
+     * @param outputStream
+     * The data stream to which the object will be written.
+     */
+    public void writeObject(Object object, OutputStream outputStream) throws IOException, SerializationException;
+
+    /**
+     * Returns the MIME type of the data read and written by this serializer.
+     */
+    public String getMIMEType();
+}

Added: incubator/pivot/tags/v1.0.1/core/src/pivot/serialization/package.html
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/core/src/pivot/serialization/package.html?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/core/src/pivot/serialization/package.html (added)
+++ incubator/pivot/tags/v1.0.1/core/src/pivot/serialization/package.html Mon Mar 16 16:36:10 2009
@@ -0,0 +1,6 @@
+<html>
+<head></head>
+<body>
+<p>Contains a set of classes for use in data serialization.</p>
+</body>
+</html>

Added: incubator/pivot/tags/v1.0.1/core/src/pivot/util/Base64.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/core/src/pivot/util/Base64.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/core/src/pivot/util/Base64.java (added)
+++ incubator/pivot/tags/v1.0.1/core/src/pivot/util/Base64.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.util;
+
+/**
+ * Implements the "base64" binary encoding scheme as defined by
+ * <a href="http://tools.ietf.org/html/rfc2045">RFC 2045</a>.
+ *
+ * @author tvolkert
+ */
+public final class Base64 {
+    private static final char[] lookup = new char[64];
+    private static final byte[] reverseLookup = new byte[256];
+
+    static {
+        // Populate the lookup array
+
+        for (int i = 0; i < 26; i++) {
+            lookup[i] = (char)('A' + i);
+        }
+
+        for (int i = 26, j = 0; i < 52; i++, j++) {
+            lookup[i] = (char)('a' + j);
+        }
+
+        for (int i = 52, j = 0; i < 62; i++, j++) {
+            lookup[i] = (char)('0' + j);
+        }
+
+        lookup[62] = '+';
+        lookup[63] = '/';
+
+        // Populate the reverse lookup array
+
+        for (int i = 0; i < 256; i++) {
+            reverseLookup[i] = -1;
+        }
+
+        for (int i = 'Z'; i >= 'A'; i--) {
+            reverseLookup[i] = (byte)(i - 'A');
+        }
+
+        for (int i = 'z'; i >= 'a'; i--) {
+            reverseLookup[i] = (byte)(i - 'a' + 26);
+        }
+
+        for (int i = '9'; i >= '0'; i--) {
+            reverseLookup[i] = (byte)(i - '0' + 52);
+        }
+
+        reverseLookup['+'] = 62;
+        reverseLookup['/'] = 63;
+        reverseLookup['='] = 0;
+    }
+
+    /**
+     * This class is not instantiable.
+     */
+    private Base64() {
+    }
+
+    /**
+     * Encodes the specified data into a base64 string.
+     *
+     * @param bytes
+     * The unencoded raw data.
+     */
+    public static String encode(byte[] bytes) {
+        StringBuilder buf = new StringBuilder(4 * (bytes.length / 3 + 1));
+
+        for (int i = 0, n = bytes.length; i < n; ) {
+            byte byte0 = bytes[i++];
+            byte byte1 = (i++ < n) ? bytes[i - 1] : 0;
+            byte byte2 = (i++ < n) ? bytes[i - 1] : 0;
+
+            buf.append(lookup[byte0 >> 2]);
+            buf.append(lookup[((byte0 << 4) | byte1 >> 4) & 63]);
+            buf.append(lookup[((byte1 << 2) | byte2 >> 6) & 63]);
+            buf.append(lookup[byte2 & 63]);
+
+            if (i > n) {
+                for (int m = buf.length(), j = m - (i - n); j < m; j++) {
+                    buf.setCharAt(j, '=');
+                }
+            }
+        }
+
+        return buf.toString();
+    }
+
+    /**
+     * Decodes the specified base64 string back into its raw data.
+     *
+     * @param encoded
+     * The base64 encoded string.
+     */
+    public static byte[] decode(String encoded) {
+        int padding = 0;
+
+        for (int i = encoded.length() - 1; encoded.charAt(i) == '='; i--) {
+            padding++;
+        }
+
+        int length = encoded.length() * 6 / 8 - padding;
+        byte[] bytes = new byte[length];
+
+        for (int i = 0, index = 0, n = encoded.length(); i < n; i += 4) {
+            int word = reverseLookup[encoded.charAt(i)] << 18;
+            word += reverseLookup[encoded.charAt(i + 1)] << 12;
+            word += reverseLookup[encoded.charAt(i + 2)] << 6;
+            word += reverseLookup[encoded.charAt(i + 3)];
+
+            for (int j = 0; j < 3 && index + j < length; j++) {
+                bytes[index + j] = (byte)(word >> (8 * (2 - j)));
+            }
+
+            index += 3;
+        }
+
+        return bytes;
+    }
+}

Added: incubator/pivot/tags/v1.0.1/core/src/pivot/util/CalendarDate.java
URL: http://svn.apache.org/viewvc/incubator/pivot/tags/v1.0.1/core/src/pivot/util/CalendarDate.java?rev=754936&view=auto
==============================================================================
--- incubator/pivot/tags/v1.0.1/core/src/pivot/util/CalendarDate.java (added)
+++ incubator/pivot/tags/v1.0.1/core/src/pivot/util/CalendarDate.java Mon Mar 16 16:36:10 2009
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.util;
+
+import java.io.Serializable;
+import java.text.NumberFormat;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * <tt>CalendarDate</tt> allows a specific day to be identified within the
+ * gregorian calendar system. This identification has no association with any
+ * particular time zone and no notion of the time of day.
+ *
+ * @author tvolkert
+ */
+public class CalendarDate implements Comparable<CalendarDate>, Serializable {
+    private static final long serialVersionUID = 3974393986540543704L;
+
+    private int year;
+    private int month;
+    private int day;
+
+    private static final int[] MONTH_LENGTHS = {
+        31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+    };
+
+    private static final int GREGORIAN_CUTOVER_YEAR = 1582;
+
+    /**
+     * Creates a new <tt>CalendarDate</tt> representing the current day in the
+     * default timezone and the default locale.
+     */
+    public CalendarDate() {
+        this(new GregorianCalendar());
+    }
+
+    /**
+     * Creates a new <tt>CalendarDate</tt> representing the day contained in
+     * the specified gregorian calendar (assuming the default locale and the
+     * default timezone).
+     *
+     * @param calendar
+     * The calendar containing the year, month, and day fields.
+     */
+    public CalendarDate(GregorianCalendar calendar) {
+        this(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH),
+            calendar.get(Calendar.DAY_OF_MONTH) - 1);
+    }
+
+    /**
+     * Creates a new <tt>CalendarDate</tt> representing the specified year,
+     * month, and day of month.
+     *
+     * @param year
+     * The year field. (e.g. <tt>2008</tt>)
+     *
+     * @param month
+     * The month field, 0-based. (e.g. <tt>2</tt> for March)
+     *
+     * @param day
+     * The day of the month, 0-based. (e.g. <tt>14</tt> for the 15th)
+     */
+    public CalendarDate(int year, int month, int day) {
+        set(year, month, day);
+    }
+
+    /**
+     * Creates a new date representing the specified date string. The date
+     * string must be in the <tt>ISO 8601</tt> "calendar date" format,
+     * which is <tt>[YYYY]-[MM]-[DD]</tt>.
+     *
+     * @param date
+     * A string in the form of <tt>[YYYY]-[MM]-[DD]</tt>. (e.g. 2008-07-23)
+     */
+    public CalendarDate(String date) {
+        Pattern pattern = Pattern.compile("^(\\d{4})-(\\d{2})-(\\d{2})$");
+        Matcher matcher = pattern.matcher(date);
+
+        if (!matcher.matches()) {
+            throw new IllegalArgumentException("Invalid date format: " + date);
+        }
+
+        String year = matcher.group(1);
+        String month = matcher.group(2);
+        String day = matcher.group(3);
+
+        set(Integer.parseInt(year), Integer.parseInt(month) - 1,
+            Integer.parseInt(day) - 1);
+    }
+
+    /**
+     * Sets this date's inner fields.
+     */
+    private void set(int year, int month, int day) {
+        if (year <= GREGORIAN_CUTOVER_YEAR || year > 9999) {
+            throw new IllegalArgumentException("Invalid year: " + year);
+        }
+
+        if (month < 0 || month > 11) {
+            throw new IllegalArgumentException("Invalid month: " + month);
+        }
+
+        int daysInMonth = MONTH_LENGTHS[month];
+
+        boolean isLeapYear = ((year & 3) == 0 && (year % 100 != 0 || year % 400 == 0));
+        if (isLeapYear && month == 1) {
+            daysInMonth++;
+        }
+
+        if (day < 0 || day >= daysInMonth) {
+            throw new IllegalArgumentException("Invalid day: " + day);
+        }
+
+        this.year = year;
+        this.month = month;
+        this.day = day;
+    }
+
+    /**
+     * Gets the year field. (e.g. <tt>2008</tt>).
+     *
+     * @return
+     * This calendar date's <tt>year</tt> field
+     */
+    public int getYear() {
+        return year;
+    }
+
+    /**
+     * Gets the month field, 0-based. (e.g. <tt>2</tt> for March).
+     *
+     * @return
+     * This calendar date's <tt>month</tt> field
+     */
+    public int getMonth() {
+        return month;
+    }
+
+    /**
+     * Gets the day of the month, 0-based. (e.g. <tt>14</tt> for the 15th).
+     *
+     * @return
+     * This calendar date's <tt>day</tt> field
+     */
+    public int getDay() {
+        return day;
+    }
+
+    /**
+     * Compares this calendar date with another calendar date.
+     *
+     * @param calendarDate
+     * The calendar date against which to compare
+     *
+     * @return
+     * A negative number, zero, or a positive number if the specified calendar
+     * date is less than, equal to, or greater than this calendar date,
+     * respectively.
+     */
+    public int compareTo(CalendarDate calendarDate) {
+        int result = year - calendarDate.year;
+
+        if (result == 0) {
+            result = month - calendarDate.month;
+
+            if (result == 0) {
+                result = day - calendarDate.day;
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Adds the specified number of days to this calendar date and returns the
+     * resulting calendar date. The number of days may be negative, in which
+     * case the result will be a date before this calendar date.
+     * <p>
+     * More formally, it is defined that given calendar dates <tt>c1</tt> and
+     * <tt>c2</tt>, the following will return <tt>true</tt>:
+     * <pre>
+     *    c1.add(c2.subtract(c1)).equals(c2);
+     * </pre>
+     *
+     * @param days
+     * The number of days to add to (or subtract from if negative) this
+     * calendar date
+     *
+     * @return
+     * The resulting calendar date
+     */
+    public CalendarDate add(int days) {
+        GregorianCalendar calendar = toCalendar();
+        calendar.add(Calendar.DAY_OF_YEAR, days);
+        return new CalendarDate(calendar);
+    }
+
+    /**
+     * Gets the number of days in between this calendar date and the specified
+     * calendar date. If this calendar date represents a day after the
+     * specified calendar date, the difference will be positive. If this
+     * calendardate represents a day before the specified calendar date, the
+     * difference will be negative. If the two calendar dates represent the
+     * same day, the difference will be zero.
+     * <p>
+     * More formally, it is defined that given calendar dates <tt>c1</tt> and
+     * <tt>c2</tt>, the following will return <tt>true</tt>:
+     * <pre>
+     *    c1.add(c2.subtract(c1)).equals(c2);
+     * </pre>
+     *
+     * @param calendarDate
+     * The calendar date to subtract from this calendar date
+     *
+     * @return
+     * The number of days in between this calendar date and
+     * <tt>calendarDate</tt>
+     */
+    public int subtract(CalendarDate calendarDate) {
+        GregorianCalendar c1 = toCalendar();
+        GregorianCalendar c2 = calendarDate.toCalendar();
+
+        long t1 = c1.getTimeInMillis();
+        long t2 = c2.getTimeInMillis();
+
+        return (int)((t1 - t2) / (1000l * 60 * 60 * 24));
+    }
+
+    /**
+     * Translates this calendar date to an instance of
+     * <tt>GregorianCalendar</tt>, with the <tt>year</tt>, <tt>month</tt>, and
+     * <tt>dayOfMonth</tt> fields set in the default time zone with the default
+     * locale.
+     *
+     * @return
+     * This calendar date as a <tt>GregorianCalendar</tt>
+     */
+    public GregorianCalendar toCalendar() {
+        return new GregorianCalendar(year, month, day + 1);
+    }
+
+    /**
+     * Indicates whether some other object is "equal to" this one.
+     * This is the case if the object is a calendar date that represents the
+     * same day as this one.
+     *
+     * @param o
+     * Reference to the object against which to compare
+     */
+    @Override
+    public boolean equals(Object o) {
+        return (o instanceof CalendarDate
+            && ((CalendarDate)o).year == year
+            && ((CalendarDate)o).month == month
+            && ((CalendarDate)o).day == day);
+    }
+
+    /**
+     * Returns a hash code value for the object.
+     */
+    @Override
+    public int hashCode() {
+        Integer hashKey = year + month + day;
+        return hashKey.hashCode();
+    }
+
+    /**
+     * Returns a string representation of this calendar date in the <tt>ISO
+     * 8601</tt> "calendar date" format, which is <tt>[YYYY]-[MM]-[DD]</tt>.
+     */
+    @Override
+    public String toString() {
+        StringBuilder buf = new StringBuilder();
+
+        NumberFormat format = NumberFormat.getIntegerInstance();
+        format.setGroupingUsed(false);
+        format.setMinimumIntegerDigits(4);
+
+        buf.append(format.format(year));
+        buf.append("-");
+
+        format.setMinimumIntegerDigits(2);
+
+        buf.append(format.format(month + 1));
+        buf.append("-");
+        buf.append(format.format(day + 1));
+
+        return buf.toString();
+    }
+}