001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    package org.apache.hadoop.io;
019    
020    import java.io.DataInput;
021    import java.io.DataOutput;
022    import java.io.IOException;
023    import java.util.Collection;
024    import java.util.HashMap;
025    import java.util.Map;
026    import java.util.Set;
027    
028    import org.apache.hadoop.classification.InterfaceAudience;
029    import org.apache.hadoop.classification.InterfaceStability;
030    import org.apache.hadoop.util.ReflectionUtils;
031    
032    /**
033     * A Writable Map.
034     */
035    @InterfaceAudience.Public
036    @InterfaceStability.Stable
037    public class MapWritable extends AbstractMapWritable
038      implements Map<Writable, Writable> {
039    
040      private Map<Writable, Writable> instance;
041      
042      /** Default constructor. */
043      public MapWritable() {
044        super();
045        this.instance = new HashMap<Writable, Writable>();
046      }
047      
048      /**
049       * Copy constructor.
050       * 
051       * @param other the map to copy from
052       */
053      public MapWritable(MapWritable other) {
054        this();
055        copy(other);
056      }
057      
058      /** {@inheritDoc} */
059      public void clear() {
060        instance.clear();
061      }
062    
063      /** {@inheritDoc} */
064      public boolean containsKey(Object key) {
065        return instance.containsKey(key);
066      }
067    
068      /** {@inheritDoc} */
069      public boolean containsValue(Object value) {
070        return instance.containsValue(value);
071      }
072    
073      /** {@inheritDoc} */
074      public Set<Map.Entry<Writable, Writable>> entrySet() {
075        return instance.entrySet();
076      }
077    
078      /** {@inheritDoc} */
079      public boolean equals(Object obj) {
080        if (this == obj) {
081          return true;
082        }
083    
084        if (obj instanceof MapWritable) {
085          Map map = (Map) obj;
086          if (size() != map.size()) {
087            return false;
088          }
089    
090          return entrySet().equals(map.entrySet());
091        }
092    
093        return false;
094      }
095    
096      /** {@inheritDoc} */
097      public Writable get(Object key) {
098        return instance.get(key);
099      }
100      
101      /** {@inheritDoc} */
102      public int hashCode() {
103        return 1 + this.instance.hashCode();
104      }
105    
106      /** {@inheritDoc} */
107      public boolean isEmpty() {
108        return instance.isEmpty();
109      }
110    
111      /** {@inheritDoc} */
112      public Set<Writable> keySet() {
113        return instance.keySet();
114      }
115    
116      /** {@inheritDoc} */
117      @SuppressWarnings("unchecked")
118      public Writable put(Writable key, Writable value) {
119        addToMap(key.getClass());
120        addToMap(value.getClass());
121        return instance.put(key, value);
122      }
123    
124      /** {@inheritDoc} */
125      public void putAll(Map<? extends Writable, ? extends Writable> t) {
126        for (Map.Entry<? extends Writable, ? extends Writable> e: t.entrySet()) {
127          put(e.getKey(), e.getValue());
128        }
129      }
130    
131      /** {@inheritDoc} */
132      public Writable remove(Object key) {
133        return instance.remove(key);
134      }
135    
136      /** {@inheritDoc} */
137      public int size() {
138        return instance.size();
139      }
140    
141      /** {@inheritDoc} */
142      public Collection<Writable> values() {
143        return instance.values();
144      }
145      
146      // Writable
147      
148      /** {@inheritDoc} */
149      @Override
150      public void write(DataOutput out) throws IOException {
151        super.write(out);
152        
153        // Write out the number of entries in the map
154        
155        out.writeInt(instance.size());
156    
157        // Then write out each key/value pair
158        
159        for (Map.Entry<Writable, Writable> e: instance.entrySet()) {
160          out.writeByte(getId(e.getKey().getClass()));
161          e.getKey().write(out);
162          out.writeByte(getId(e.getValue().getClass()));
163          e.getValue().write(out);
164        }
165      }
166    
167      /** {@inheritDoc} */
168      @SuppressWarnings("unchecked")
169      @Override
170      public void readFields(DataInput in) throws IOException {
171        super.readFields(in);
172        
173        // First clear the map.  Otherwise we will just accumulate
174        // entries every time this method is called.
175        this.instance.clear();
176        
177        // Read the number of entries in the map
178        
179        int entries = in.readInt();
180        
181        // Then read each key/value pair
182        
183        for (int i = 0; i < entries; i++) {
184          Writable key = (Writable) ReflectionUtils.newInstance(getClass(
185              in.readByte()), getConf());
186          
187          key.readFields(in);
188          
189          Writable value = (Writable) ReflectionUtils.newInstance(getClass(
190              in.readByte()), getConf());
191          
192          value.readFields(in);
193          instance.put(key, value);
194        }
195      }
196    }