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 019package org.apache.hadoop.io; 020 021import java.io.DataInput; 022import java.io.DataOutput; 023import java.io.IOException; 024import java.util.EnumSet; 025import java.util.Iterator; 026import java.util.Collection; 027import java.util.AbstractCollection; 028 029import org.apache.hadoop.classification.InterfaceAudience; 030import org.apache.hadoop.classification.InterfaceStability; 031import org.apache.hadoop.conf.Configurable; 032import org.apache.hadoop.conf.Configuration; 033 034/** A Writable wrapper for EnumSet. */ 035@InterfaceAudience.Public 036@InterfaceStability.Stable 037public 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}