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