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 }