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
019 package org.apache.hadoop.io;
020
021 import java.io.DataInput;
022 import java.io.DataOutput;
023 import java.io.IOException;
024 import java.util.EnumSet;
025 import java.util.Iterator;
026 import java.util.Collection;
027 import java.util.AbstractCollection;
028
029 import org.apache.hadoop.classification.InterfaceAudience;
030 import org.apache.hadoop.classification.InterfaceStability;
031 import org.apache.hadoop.conf.Configurable;
032 import org.apache.hadoop.conf.Configuration;
033
034 /** A Writable wrapper for EnumSet. */
035 @InterfaceAudience.Public
036 @InterfaceStability.Stable
037 public class EnumSetWritable<E extends Enum<E>> extends AbstractCollection<E>
038 implements Writable, Configurable {
039
040 private EnumSet<E> value;
041
042 private transient Class<E> elementType;
043
044 private transient Configuration conf;
045
046 EnumSetWritable() {
047 }
048
049 public Iterator<E> iterator() { return value.iterator(); }
050 public int size() { return value.size(); }
051 public boolean add(E e) {
052 if (value == null) {
053 value = EnumSet.of(e);
054 set(value, null);
055 }
056 return value.add(e);
057 }
058
059 /**
060 * Construct a new EnumSetWritable. If the <tt>value</tt> argument is null or
061 * its size is zero, the <tt>elementType</tt> argument must not be null. If
062 * the argument <tt>value</tt>'s size is bigger than zero, the argument
063 * <tt>elementType</tt> is not be used.
064 *
065 * @param value
066 * @param elementType
067 */
068 public EnumSetWritable(EnumSet<E> value, Class<E> elementType) {
069 set(value, elementType);
070 }
071
072 /**
073 * Construct a new EnumSetWritable. Argument <tt>value</tt> should not be null
074 * or empty.
075 *
076 * @param value
077 */
078 public EnumSetWritable(EnumSet<E> value) {
079 this(value, null);
080 }
081
082 /**
083 * reset the EnumSetWritable with specified
084 * <tt>value</value> and <tt>elementType</tt>. If the <tt>value</tt> argument
085 * is null or its size is zero, the <tt>elementType</tt> argument must not be
086 * null. If the argument <tt>value</tt>'s size is bigger than zero, the
087 * argument <tt>elementType</tt> is not be used.
088 *
089 * @param value
090 * @param elementType
091 */
092 public void set(EnumSet<E> value, Class<E> elementType) {
093 if ((value == null || value.size() == 0)
094 && (this.elementType == null && elementType == null)) {
095 throw new IllegalArgumentException(
096 "The EnumSet argument is null, or is an empty set but with no elementType provided.");
097 }
098 this.value = value;
099 if (value != null && value.size() > 0) {
100 Iterator<E> iterator = value.iterator();
101 this.elementType = iterator.next().getDeclaringClass();
102 } else if (elementType != null) {
103 this.elementType = elementType;
104 }
105 }
106
107 /** Return the value of this EnumSetWritable. */
108 public EnumSet<E> get() {
109 return value;
110 }
111
112 /** {@inheritDoc} */
113 @SuppressWarnings("unchecked")
114 public void readFields(DataInput in) throws IOException {
115 int length = in.readInt();
116 if (length == -1)
117 this.value = null;
118 else if (length == 0) {
119 this.elementType = (Class<E>) ObjectWritable.loadClass(conf,
120 WritableUtils.readString(in));
121 this.value = EnumSet.noneOf(this.elementType);
122 } else {
123 E first = (E) ObjectWritable.readObject(in, conf);
124 this.value = (EnumSet<E>) EnumSet.of(first);
125 for (int i = 1; i < length; i++)
126 this.value.add((E) ObjectWritable.readObject(in, conf));
127 }
128 }
129
130 /** {@inheritDoc} */
131 public void write(DataOutput out) throws IOException {
132 if (this.value == null) {
133 out.writeInt(-1);
134 WritableUtils.writeString(out, this.elementType.getName());
135 } else {
136 Object[] array = this.value.toArray();
137 int length = array.length;
138 out.writeInt(length);
139 if (length == 0) {
140 if (this.elementType == null)
141 throw new UnsupportedOperationException(
142 "Unable to serialize empty EnumSet with no element type provided.");
143 WritableUtils.writeString(out, this.elementType.getName());
144 }
145 for (int i = 0; i < length; i++) {
146 ObjectWritable.writeObject(out, array[i], array[i].getClass(), conf);
147 }
148 }
149 }
150
151 /**
152 * Returns true if <code>o</code> is an EnumSetWritable with the same value,
153 * or both are null.
154 */
155 public boolean equals(Object o) {
156 if (o == null) {
157 throw new IllegalArgumentException("null argument passed in equal().");
158 }
159
160 if (!(o instanceof EnumSetWritable))
161 return false;
162
163 EnumSetWritable<?> other = (EnumSetWritable<?>) o;
164
165 if (this == o || (this.value == other.value))
166 return true;
167 if (this.value == null) // other.value must not be null if we reach here
168 return false;
169
170 return this.value.equals(other.value);
171 }
172
173 /**
174 * Returns the class of all the elements of the underlying EnumSetWriable. It
175 * may return null.
176 *
177 * @return the element class
178 */
179 public Class<E> getElementType() {
180 return elementType;
181 }
182
183 /** {@inheritDoc} */
184 public int hashCode() {
185 if (value == null)
186 return 0;
187 return (int) value.hashCode();
188 }
189
190 /** {@inheritDoc} */
191 public String toString() {
192 if (value == null)
193 return "(null)";
194 return value.toString();
195 }
196
197 /** {@inheritDoc} */
198 @Override
199 public Configuration getConf() {
200 return this.conf;
201 }
202
203 /** {@inheritDoc} */
204 @Override
205 public void setConf(Configuration conf) {
206 this.conf = conf;
207 }
208
209 static {
210 WritableFactories.setFactory(EnumSetWritable.class, new WritableFactory() {
211 @SuppressWarnings("unchecked")
212 @Override
213 public Writable newInstance() {
214 return new EnumSetWritable();
215 }
216 });
217 }
218 }