harmony-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From py...@apache.org
Subject svn commit: r438409 - in /incubator/harmony/enhanced/classlib/trunk/modules/luni/src: main/java/java/util/EnumMap.java test/java/tests/api/java/util/EnumMapTest.java
Date Wed, 30 Aug 2006 07:27:37 GMT
Author: pyang
Date: Wed Aug 30 00:27:35 2006
New Revision: 438409

URL: http://svn.apache.org/viewvc?rev=438409&view=rev
Log:
Patch applied for HARMONY-1341 ([classlib][luni] new method entrySet() in java.util.EnumMap)

Modified:
    incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/util/EnumMap.java
    incubator/harmony/enhanced/classlib/trunk/modules/luni/src/test/java/tests/api/java/util/EnumMapTest.java

Modified: incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/util/EnumMap.java
URL: http://svn.apache.org/viewvc/incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/util/EnumMap.java?rev=438409&r1=438408&r2=438409&view=diff
==============================================================================
--- incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/util/EnumMap.java
(original)
+++ incubator/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/java/util/EnumMap.java
Wed Aug 30 00:27:35 2006
@@ -16,6 +16,7 @@
 package java.util;
 
 import java.io.Serializable;
+import java.lang.reflect.Array;
 
 import org.apache.harmony.luni.util.NotYetImplementedException;
 
@@ -23,27 +24,101 @@
         Map<K, V>, Serializable, Cloneable {
     private static final long serialVersionUID = 458661240069192865L;
 
-    Class<K> keyType;
+    private Class<K> keyType;
 
-    transient Enum keys[];
+    transient Enum[] keys;
 
-    transient Object values[];
+    transient Object[] values;
 
-    transient boolean hasMapping[];
+    transient boolean[] hasMapping;
 
-    transient int mappingsCount;
+    private transient int mappingsCount;
 
     transient int enumSize;
 
+    private transient EnumMapEntrySet<K, V> entrySet = null;
+
+    private static class Entry<KT extends Enum<KT>, VT> extends
+            MapEntry<KT, VT> {
+        private final EnumMap<KT, VT> enumMap;
+
+        private final int ordinal;
+
+        Entry(KT theKey, VT theValue, EnumMap<KT, VT> em) {
+            super(theKey, theValue);
+            enumMap = em;
+            ordinal = ((Enum) theKey).ordinal();
+        }
+
+        @Override
+        public boolean equals(Object object) {
+            if (!enumMap.hasMapping[ordinal]) {
+                return false;
+            }
+            boolean isEqual = false;
+            if (object instanceof Map.Entry) {
+                Map.Entry<KT, VT> entry = (Map.Entry<KT, VT>) object;
+                Object enumKey = entry.getKey();
+                if (key.equals(enumKey)) {
+                    Object theValue = entry.getValue();
+                    isEqual = enumMap.values[ordinal] == null ? null == theValue
+                            : enumMap.values[ordinal].equals(theValue);
+                }
+            }
+            return isEqual;
+        }
+
+        @Override
+        public int hashCode() {
+            return (enumMap.keys[ordinal] == null ? 0
+                    : enumMap.keys[ordinal].hashCode())
+                    ^ (enumMap.values[ordinal] == null ? 0
+                            : enumMap.values[ordinal].hashCode());
+        }
+
+        @Override
+        public KT getKey() {
+            checkEntryStatus();
+            return (KT) enumMap.keys[ordinal];
+        }
+
+        @Override
+        public VT getValue() {
+            checkEntryStatus();
+            return (VT) enumMap.values[ordinal];
+        }
+
+        @Override
+        public VT setValue(VT value) {
+            checkEntryStatus();
+            return enumMap.put((KT) enumMap.keys[ordinal], value);
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder result = new StringBuilder(enumMap.keys[ordinal]
+                    .toString());
+            result.append("="); //$NON-NLS-1$
+            result.append(enumMap.values[ordinal].toString());
+            return result.toString();
+        }
+
+        private void checkEntryStatus() {
+            if (!enumMap.hasMapping[ordinal]) {
+                throw new IllegalStateException();
+            }
+        }
+    }
+
     private static class EnumMapIterator<E, KT extends Enum<KT>, VT> implements
             Iterator<E> {
-        private int position = 0;
+        int position = 0;
 
-        private int prePosition = -1;
+        int prePosition = -1;
 
-        private final EnumMap<KT, VT> enumMap;
+        final EnumMap<KT, VT> enumMap;
 
-        private final MapEntry.Type<E, KT, VT> type;
+        final MapEntry.Type<E, KT, VT> type;
 
         EnumMapIterator(MapEntry.Type<E, KT, VT> value, EnumMap<KT, VT> em) {
             enumMap = em;
@@ -89,7 +164,7 @@
                             enumMap.values[prePosition])).toString();
         }
 
-        void checkStatus() {
+        private void checkStatus() {
             if (-1 == prePosition) {
                 throw new IllegalStateException();
             }
@@ -197,6 +272,103 @@
         }
     }
 
+    private static class EnumMapEntryIterator<E, KT extends Enum<KT>, VT>
+            extends EnumMapIterator<E, KT, VT> {
+        EnumMapEntryIterator(MapEntry.Type<E, KT, VT> value, EnumMap<KT, VT>
em) {
+            super(value, em);
+        }
+
+        @Override
+        public E next() {
+            if (!hasNext()) {
+                throw new NoSuchElementException();
+            }
+            prePosition = position++;
+            return type.get(new Entry<KT, VT>((KT) enumMap.keys[prePosition],
+                    (VT) enumMap.values[prePosition], enumMap));
+        }
+    }
+
+    private static class EnumMapEntrySet<KT extends Enum<KT>, VT> extends
+            AbstractSet<Map.Entry<KT, VT>> {
+        private final EnumMap<KT, VT> enumMap;
+
+        EnumMapEntrySet(EnumMap<KT, VT> em) {
+            enumMap = em;
+        }
+
+        @Override
+        public void clear() {
+            enumMap.clear();
+        }
+
+        @Override
+        public boolean contains(Object object) {
+            boolean isEqual = false;
+            if (object instanceof Map.Entry) {
+                Object enumKey = ((Map.Entry) object).getKey();
+                Object enumValue = ((Map.Entry) object).getValue();
+                if (enumMap.containsKey(enumKey)) {
+                    VT value = enumMap.get(enumKey);
+                    isEqual = (value == null ? null == enumValue : value
+                            .equals(enumValue));
+                }
+            }
+            return isEqual;
+        }
+
+        @Override
+        public Iterator<Map.Entry<KT, VT>> iterator() {
+            return new EnumMapEntryIterator<Map.Entry<KT, VT>, KT, VT>(
+                    new MapEntry.Type<Map.Entry<KT, VT>, KT, VT>() {
+                        public Map.Entry<KT, VT> get(MapEntry<KT, VT> entry)
{
+                            return entry;
+                        }
+                    }, enumMap);
+        }
+
+        @Override
+        public boolean remove(Object object) {
+            if (contains(object)) {
+                enumMap.remove(((Map.Entry) object).getKey());
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public int size() {
+            return enumMap.size();
+        }
+
+        @Override
+        public Object[] toArray() {
+            Object[] entryArray = new Object[enumMap.size()];
+            return toArray(entryArray);
+        }
+
+        @Override
+        public Object[] toArray(Object[] array) {
+            int size = enumMap.size();
+            int index = 0;
+            Object[] entryArray = array;
+            if (size > array.length) {
+                Class clazz = array.getClass().getComponentType();
+                entryArray = (Object[]) Array.newInstance(clazz, size);
+            }
+            Iterator iter = iterator();
+            for (; index < size; index++) {
+                Map.Entry<KT, VT> entry = (Map.Entry<KT, VT>) iter.next();
+                entryArray[index] = new MapEntry<KT, VT>((KT) entry.getKey(),
+                        (VT) entry.getValue());
+            }
+            if (index < array.length) {
+                entryArray[index] = null;
+            }
+            return entryArray;
+        }
+    }
+
     /**
      * Constructs an empty enum map using the given key type.
      * 
@@ -324,8 +496,12 @@
      * 
      * @return a set view of the mappings contained in this map.
      */
+    @Override
     public Set<Map.Entry<K, V>> entrySet() {
-        throw new NotYetImplementedException();
+        if (null == entrySet) {
+            entrySet = new EnumMapEntrySet<K, V>(this);
+        }
+        return entrySet;
     }
 
     /**

Modified: incubator/harmony/enhanced/classlib/trunk/modules/luni/src/test/java/tests/api/java/util/EnumMapTest.java
URL: http://svn.apache.org/viewvc/incubator/harmony/enhanced/classlib/trunk/modules/luni/src/test/java/tests/api/java/util/EnumMapTest.java?rev=438409&r1=438408&r2=438409&view=diff
==============================================================================
--- incubator/harmony/enhanced/classlib/trunk/modules/luni/src/test/java/tests/api/java/util/EnumMapTest.java
(original)
+++ incubator/harmony/enhanced/classlib/trunk/modules/luni/src/test/java/tests/api/java/util/EnumMapTest.java
Wed Aug 30 00:27:35 2006
@@ -24,8 +24,6 @@
 import java.util.NoSuchElementException;
 import java.util.Set;
 
-import org.apache.harmony.luni.util.NotYetImplementedException;
-
 import junit.framework.TestCase;
 
 public class EnumMapTest extends TestCase {
@@ -40,7 +38,38 @@
     enum Empty {
         //Empty
     }
-    
+
+    private static class MockEntry<K, V> implements Map.Entry<K, V> {
+        private K key;
+
+        private V value;
+
+        public MockEntry(K key, V value) {
+            this.key = key;
+            this.value = value;
+        }
+
+        @Override
+        public int hashCode() {
+            return (key == null ? 0 : key.hashCode())
+                    ^ (value == null ? 0 : value.hashCode());
+        }
+
+        public K getKey() {
+            return key;
+        }
+
+        public V getValue() {
+            return value;
+        }
+
+        public V setValue(V object) {
+            V oldValue = value;
+            value = object;
+            return oldValue;
+        }
+    }
+
 
     /**
      * @tests java.util.EnumMap#EnumMap(Class)
@@ -265,6 +294,236 @@
     }
 
     /**
+     * @tests java.util.EnumMap#entrySet()
+     */
+    @SuppressWarnings({ "unchecked", "boxing" })
+    public void test_entrySet() {
+        EnumMap enumSizeMap = new EnumMap(Size.class);
+        enumSizeMap.put(Size.Middle, 1);
+        enumSizeMap.put(Size.Big, null);
+        MockEntry mockEntry = new MockEntry(Size.Middle, 1);
+        Set set = enumSizeMap.entrySet();
+
+        Set set1 = enumSizeMap.entrySet();
+        assertSame("Should be same", set1, set); //$NON-NLS-1$
+        try {
+            set.add(mockEntry);
+            fail("Should throw UnsupportedOperationException"); //$NON-NLS-1$
+        } catch (UnsupportedOperationException e) {
+            // Expected
+        }
+
+        assertTrue("Returned false for contained object", set//$NON-NLS-1$
+                .contains(mockEntry));
+        mockEntry = new MockEntry(Size.Middle, null);
+        assertFalse("Returned true for uncontained object", set //$NON-NLS-1$
+                .contains(mockEntry));
+        assertFalse("Returned true for uncontained object", set //$NON-NLS-1$
+                .contains(Size.Small));
+        mockEntry = new MockEntry(new Integer(1), 1);
+        assertFalse("Returned true for uncontained object", set //$NON-NLS-1$
+                .contains(mockEntry));
+        assertFalse("Returned true for uncontained object", set //$NON-NLS-1$
+                .contains(new Integer(1)));
+
+        mockEntry = new MockEntry(Size.Big, null);
+        assertTrue("Returned false for contained object", set//$NON-NLS-1$
+                .contains(mockEntry));
+        assertTrue("Returned false when the object can be removed", set //$NON-NLS-1$
+                .remove(mockEntry));
+        assertFalse("Returned true for uncontained object", set //$NON-NLS-1$
+                .contains(mockEntry));
+        assertFalse("Returned true when the object can not be removed", set //$NON-NLS-1$
+                .remove(mockEntry));
+        mockEntry = new MockEntry(new Integer(1), 1);
+        assertFalse("Returned true when the object can not be removed", set //$NON-NLS-1$
+                .remove(mockEntry));
+        assertFalse("Returned true when the object can not be removed", set //$NON-NLS-1$
+                .remove(new Integer(1)));
+
+        // The set is backed by the map so changes to one are reflected by the
+        // other.
+        enumSizeMap.put(Size.Big, 3);
+        mockEntry = new MockEntry(Size.Big, 3);
+        assertTrue("Returned false for contained object", set//$NON-NLS-1$
+                .contains(mockEntry));
+        enumSizeMap.remove(Size.Big);
+        assertFalse("Returned true for uncontained object", set //$NON-NLS-1$
+                .contains(mockEntry));
+
+        assertEquals("Wrong size", 1, set.size()); //$NON-NLS-1$
+        set.clear();
+        assertEquals("Wrong size", 0, set.size()); //$NON-NLS-1$
+
+        enumSizeMap = new EnumMap(Size.class);
+        enumSizeMap.put(Size.Middle, 1);
+        enumSizeMap.put(Size.Big, null);
+        set = enumSizeMap.entrySet();
+        Collection c = new ArrayList();
+        c.add(new MockEntry(Size.Middle, 1));
+        assertTrue("Return wrong value", set.containsAll(c)); //$NON-NLS-1$
+        assertTrue("Remove does not success", set.removeAll(c)); //$NON-NLS-1$
+
+        enumSizeMap.put(Size.Middle, 1);
+        c.add(new MockEntry(Size.Big, 3));
+        assertTrue("Remove does not success", set.removeAll(c)); //$NON-NLS-1$
+        assertFalse("Should return false", set.removeAll(c)); //$NON-NLS-1$
+        assertEquals("Wrong size", 1, set.size()); //$NON-NLS-1$
+
+        enumSizeMap = new EnumMap(Size.class);
+        enumSizeMap.put(Size.Middle, 1);
+        enumSizeMap.put(Size.Big, null);
+        set = enumSizeMap.entrySet();
+        c = new ArrayList();
+        c.add(new MockEntry(Size.Middle, 1));
+        c.add(new MockEntry(Size.Big, 3));
+
+        assertTrue("Retain does not success", set.retainAll(c)); //$NON-NLS-1$
+        assertEquals("Wrong size", 1, set.size()); //$NON-NLS-1$
+        assertFalse("Should return false", set.retainAll(c)); //$NON-NLS-1$
+
+        enumSizeMap = new EnumMap(Size.class);
+        enumSizeMap.put(Size.Middle, 1);
+        enumSizeMap.put(Size.Big, null);
+
+        set = enumSizeMap.entrySet();
+        Object[] array = set.toArray();
+        assertEquals("Wrong length", 2, array.length); //$NON-NLS-1$
+        Map.Entry entry = (Map.Entry) array[0];
+        assertEquals("Wrong key", Size.Middle, entry.getKey()); //$NON-NLS-1$
+        assertEquals("Wrong value", 1, entry.getValue()); //$NON-NLS-1$
+
+        Object[] array1 = new Object[10];
+        array1 = set.toArray();
+        assertEquals("Wrong length", 2, array1.length); //$NON-NLS-1$
+        entry = (Map.Entry) array[0];
+        assertEquals("Wrong key", Size.Middle, entry.getKey()); //$NON-NLS-1$
+        assertEquals("Wrong value", 1, entry.getValue()); //$NON-NLS-1$
+
+        array1 = new Object[10];
+        array1 = set.toArray(array1);
+        assertEquals("Wrong length", 10, array1.length); //$NON-NLS-1$
+        entry = (Map.Entry) array[1];
+        assertEquals("Wrong key", Size.Big, entry.getKey()); //$NON-NLS-1$
+        assertNull("Should be null", array1[2]); //$NON-NLS-1$
+
+        set = enumSizeMap.entrySet();
+        Integer integer = new Integer("1"); //$NON-NLS-1$
+        assertFalse("Returned true when the object can not be removed", set //$NON-NLS-1$
+                .remove(integer));
+        assertTrue("Returned false when the object can be removed", set //$NON-NLS-1$
+                .remove(entry));
+
+        enumSizeMap = new EnumMap(Size.class);
+        enumSizeMap.put(Size.Middle, 1);
+        enumSizeMap.put(Size.Big, null);
+        set = enumSizeMap.entrySet();
+        Iterator iter = set.iterator();
+        entry = (Map.Entry) iter.next();
+        assertTrue("Returned false for contained object", set.contains(entry)); //$NON-NLS-1$
+        mockEntry = new MockEntry(Size.Middle, 2);
+        assertFalse("Returned true for uncontained object", set //$NON-NLS-1$
+                .contains(mockEntry));
+        mockEntry = new MockEntry(new Integer(2), 2);
+        assertFalse("Returned true for uncontained object", set //$NON-NLS-1$
+                .contains(mockEntry));
+        entry = (Map.Entry) iter.next();
+        assertTrue("Returned false for contained object", set.contains(entry)); //$NON-NLS-1$
+
+        enumSizeMap.put(Size.Middle, 1);
+        enumSizeMap.remove(Size.Big);
+        mockEntry = new MockEntry(Size.Big, null);
+        assertEquals("Wrong size", 1, set.size()); //$NON-NLS-1$
+        assertFalse("Returned true for uncontained object", set.contains(mockEntry)); //$NON-NLS-1$
+        enumSizeMap.put(Size.Big, 2);
+        mockEntry = new MockEntry(Size.Big, 2);
+        assertTrue("Returned false for contained object", set //$NON-NLS-1$
+                .contains(mockEntry));
+
+        iter.remove();
+        try {
+            iter.remove();
+            fail("Should throw IllegalStateException"); //$NON-NLS-1$
+        } catch (IllegalStateException e) {
+            // Expected
+        }
+        try {
+            entry.setValue(2);
+            fail("Should throw IllegalStateException"); //$NON-NLS-1$
+        } catch (IllegalStateException e) {
+            // Expected
+        }
+        try {
+            set.contains(entry);
+            fail("Should throw IllegalStateException"); //$NON-NLS-1$
+        } catch (IllegalStateException e) {
+            // Expected
+        }
+
+        enumSizeMap = new EnumMap(Size.class);
+        enumSizeMap.put(Size.Middle, 1);
+        enumSizeMap.put(Size.Big, null);
+        set = enumSizeMap.entrySet();
+        iter = set.iterator();
+        entry = (Map.Entry) iter.next();
+        assertEquals("Wrong key", Size.Middle, entry.getKey()); //$NON-NLS-1$
+
+        assertTrue("Returned false for contained object", set.contains(entry)); //$NON-NLS-1$
+        enumSizeMap.put(Size.Middle, 3);
+        assertTrue("Returned false for contained object", set.contains(entry)); //$NON-NLS-1$
+        entry.setValue(2);
+        assertTrue("Returned false for contained object", set.contains(entry)); //$NON-NLS-1$
+        assertFalse("Returned true for uncontained object", set //$NON-NLS-1$
+                .remove(new Integer(1)));
+
+        iter.next();
+        //The following test case fails on RI.
+        assertEquals("Wrong key", Size.Middle, entry.getKey()); //$NON-NLS-1$
+        set.clear();
+        assertEquals("Wrong size", 0, set.size()); //$NON-NLS-1$
+
+        enumSizeMap = new EnumMap(Size.class);
+        enumSizeMap.put(Size.Middle, 1);
+        enumSizeMap.put(Size.Big, null);
+        set = enumSizeMap.entrySet();
+        iter = set.iterator();
+        mockEntry = new MockEntry(Size.Middle, 1);
+
+        assertFalse("Wrong result", entry.equals(mockEntry)); //$NON-NLS-1$
+        try {
+            iter.remove();
+            fail("Should throw IllegalStateException"); //$NON-NLS-1$
+        } catch (IllegalStateException e) {
+            // Expected
+        }
+        entry = (Map.Entry) iter.next();
+        assertEquals("Wrong key", Size.Middle, entry.getKey()); //$NON-NLS-1$
+        assertTrue("Should return true", entry.equals(mockEntry)); //$NON-NLS-1$
+        assertEquals("Should be equal", mockEntry.hashCode(), entry.hashCode()); //$NON-NLS-1$
+        mockEntry = new MockEntry(Size.Big, 1);
+        assertFalse("Wrong result", entry.equals(mockEntry)); //$NON-NLS-1$
+
+        entry = (Map.Entry) iter.next();
+        assertFalse("Wrong result", entry.equals(mockEntry)); //$NON-NLS-1$
+        assertEquals("Wrong key", Size.Big, entry.getKey()); //$NON-NLS-1$
+        iter.remove();
+        assertFalse("Wrong result", entry.equals(mockEntry)); //$NON-NLS-1$
+        assertEquals("Wrong size", 1, set.size()); //$NON-NLS-1$
+        try {
+            iter.remove();
+            fail("Should throw IllegalStateException"); //$NON-NLS-1$
+        } catch (IllegalStateException e) {
+            // Expected
+        }
+        try {
+            iter.next();
+            fail("Should throw NoSuchElementException"); //$NON-NLS-1$
+        } catch (NoSuchElementException e) {
+            // Expected
+        }
+    }
+
+    /**
      * @tests java.util.EnumMap#keySet()
      */
     @SuppressWarnings( { "unchecked", "boxing" })
@@ -509,7 +768,7 @@
         assertEquals("Return wrong value", 2, enumColorMap.put(Color.Blue, //$NON-NLS-1$
                 new Double(4)));
     }
-    
+
     /**
      * @tests java.util.EnumMap#putAll(Map)
      */
@@ -518,31 +777,26 @@
         EnumMap enumColorMap = new EnumMap<Color, Double>(Color.class);
         enumColorMap.put(Color.Green, 2);
 
-        // TODO: The EnumMap.entrySet() will be implemented later.
+        EnumMap enumSizeMap = new EnumMap(Size.class);
+        enumColorMap.putAll(enumSizeMap);
+
+        enumSizeMap.put(Size.Big, 1);
         try {
-            EnumMap enumSizeMap = new EnumMap(Size.class);
             enumColorMap.putAll(enumSizeMap);
-
-            enumSizeMap.put(Size.Big, 1);
-            try {
-                enumColorMap.putAll(enumSizeMap);
-                fail("Expected ClassCastException"); //$NON-NLS-1$
-            } catch (ClassCastException e) {
-                // Expected
-            }
-
-            EnumMap enumColorMap1 = new EnumMap<Color, Double>(Color.class);
-            enumColorMap1.put(Color.Blue, 3);
-            enumColorMap.putAll(enumColorMap1);
-            assertEquals("Get returned incorrect value for given key", 3, //$NON-NLS-1$
-                    enumColorMap.get(Color.Blue));
-            assertEquals("Wrong Size", 2, enumColorMap.size()); //$NON-NLS-1$
-
-            enumColorMap = new EnumMap<Color, Double>(Color.class);
-        } catch (NotYetImplementedException e) {
+            fail("Expected ClassCastException"); //$NON-NLS-1$
+        } catch (ClassCastException e) {
             // Expected
         }
-        
+
+        EnumMap enumColorMap1 = new EnumMap<Color, Double>(Color.class);
+        enumColorMap1.put(Color.Blue, 3);
+        enumColorMap.putAll(enumColorMap1);
+        assertEquals("Get returned incorrect value for given key", 3, //$NON-NLS-1$
+                enumColorMap.get(Color.Blue));
+        assertEquals("Wrong Size", 2, enumColorMap.size()); //$NON-NLS-1$
+
+        enumColorMap = new EnumMap<Color, Double>(Color.class);
+
         HashMap hashColorMap = null;
         try {
             enumColorMap.putAll(hashColorMap);
@@ -562,7 +816,7 @@
                 .get(Color.Red));
         hashColorMap.put(Color.Red, new Integer(1));
         enumColorMap.putAll(hashColorMap);
-        assertEquals("Get returned incorrect value for given key", new Integer( //$NON-NLS-1$
+        assertEquals("Get returned incorrect value for given key", new Integer(//$NON-NLS-1$
                 2), enumColorMap.get(Color.Green));
         hashColorMap.put(Size.Big, 3);
         try {
@@ -581,7 +835,7 @@
             // Expected
         }
     }
-    
+
     /**
      * @tests java.util.EnumMap#remove(Object)
      */



Mime
View raw message