001/* 002 * BioJava development code 003 * 004 * This code may be freely distributed and modified under the 005 * terms of the GNU Lesser General Public Licence. This should 006 * be distributed with the code. If you do not have a copy, 007 * see: 008 * 009 * http://www.gnu.org/copyleft/lesser.html 010 * 011 * Copyright for this code is held jointly by the individual 012 * authors. These should be listed in @author doc comments. 013 * 014 * For more information on the BioJava project and its aims, 015 * or to join the biojava-l mailing list, visit the home page 016 * at: 017 * 018 * http://www.biojava.org 019 */ 020 021package org.biojava.utils; 022 023import java.io.Serializable; 024import java.lang.reflect.Field; 025import java.lang.reflect.Modifier; 026import java.util.HashSet; 027import java.util.Iterator; 028import java.util.NoSuchElementException; 029import java.util.Set; 030 031import org.biojava.bio.BioError; 032 033/** 034 * 035 * Class for all constants which are used to indicate change 036 * types. Note that all ChangeType objects must be accessible 037 * via a public static field of some class or interface. These should 038 * be specified at construction time, so that the ChangeType can 039 * be properly serialized. Typically, they should be constructed 040 * using code like: 041 * 042 * <pre> 043 * class MyClassWhichCanFireChangeEvents { 044 * public final static ChangeType CHANGE_COLOR = new ChangeType( 045 * "Color change", 046 * MyClassWhichCanFireChangeEvents.class, 047 * "CHANGE_COLOR"); 048 * // Rest of the class here... 049 * } 050 * </pre> 051 * 052 * <p> 053 * As of BioJava 1.2, the known ChangeTypes of a system follow a simple 054 * hierarchy with single inheritance. All ChangeTypes 055 * (except ChangeType.UNKNOWN) have a parent ChangeType (defaulting 056 * to ChangeType.UNKNOWN). Generally, when a listener is registered 057 * for changetype <code>foo</code>, changes of type <code>bar</code> 058 * should be accepted if <code>bar</code> is a sub-type of <code>foo</code>. 059 * This can be checked using an expression like: 060 * </p> 061 * 062 * <pre> 063 * bar.isMatchingType(foo); 064 * </pre> 065 * 066 * @author Thomas Down 067 * @author Matthew Pocock 068 * @since 1.1 069 */ 070 071public final class ChangeType implements Serializable { 072 private final String name; 073 private final Field ourField; 074 private final ChangeType superType; 075 076 /** 077 * Constant ChangeType field which indicates that a change has 078 * occured which can't otherwise be represented. Please do not 079 * use this when there is another, more sensible, option. This 080 * is the fallback for when you realy don't know what else to 081 * do. 082 * 083 * <p> 084 * As of BioJava 1.2, this type is the root of the ChangeType 085 * hierarchy. Listening for this type is equivalent to listening 086 * for all ChangeTypes. 087 * </p> 088 */ 089 public static final ChangeType UNKNOWN; 090 091 /** 092 * Construct a new ChangeType. 093 * 094 * @param name The name of this change. 095 * @param ourField The public static field which contains this 096 * ChangeType. 097 * @param superType The supertype of this type. 098 * 099 * @since 1.2 100 */ 101 102 public ChangeType(String name, Field ourField, ChangeType superType) { 103 this.name = name; 104 this.ourField = ourField; 105 this.superType = superType; 106 } 107 108 /** 109 * Construct a new ChangeType with superType UNKNOWN. 110 * 111 * @param name The name of this change. 112 * @param ourField The public static field which contains this 113 * ChangeType. 114 */ 115 public ChangeType(String name, Field ourField) { 116 this(name, ourField, ChangeType.UNKNOWN); 117 } 118 119 /** 120 * Construct a new ChangeType with supertype UNKNOWN. 121 * 122 * @param name The name of this change. 123 * @param clazz The class which is going to contain this change. 124 * @param fname 125 * The name of the field in <code>clazz</code> which 126 * is to contain a reference to this change. 127 * @throws BioError If the field cannot be found. 128 */ 129 public ChangeType(String name, Class clazz, String fname) { 130 this(name, clazz, fname, ChangeType.UNKNOWN); 131 } 132 133 /** 134 * Construct a new ChangeType. 135 * 136 * @param name The name of this change. 137 * @param clazz The class which is going to contain this change. 138 * @param fname 139 * The name of the field in <code>clazz</code> which 140 * is to contain a reference to this change. 141 * @param superType the supertype of this type. 142 * @throws BioError If the field cannot be found. 143 * 144 * @since 1.2 145 */ 146 public ChangeType(String name, Class clazz, String fname, ChangeType superType) { 147 this.name = name; 148 this.superType = superType; 149 try { 150 this.ourField = clazz.getField(fname); 151 } 152 catch (Exception ex) { 153 throw new AssertionFailure("Couldn't find field " + fname + " in class " + clazz.getName(), ex); 154 } 155 } 156 157 public ChangeType(String name, String className, String fieldName, ChangeType superType) { 158 this.name = name; 159 this.superType = superType; 160 try { 161 Class clazz = Class.forName(className); 162 this.ourField = clazz.getField(fieldName); 163 } catch (Exception ex) { 164 throw new AssertionFailure( 165 "Couldn't find class or field " + className + 166 "->" + fieldName, 167 ex 168 ); 169 } 170 } 171 172 public ChangeType(String name, String className, String fieldName) { 173 this(name, className, fieldName, ChangeType.UNKNOWN); 174 } 175 176 /** 177 * Return the name of this change. 178 * 179 * @return The Name value 180 */ 181 public String getName() { 182 return name; 183 } 184 185 /** 186 * Return a Field object where this change type is declared. 187 */ 188 189 public Field getField() { 190 return ourField; 191 } 192 193 /** 194 * Return a string representation of this change. 195 * 196 * @return Description of the Returned Value 197 */ 198 public String toString() { 199 return "ChangeType: " + name; 200 } 201 202 /** 203 * Make a placeholder for this object in a serialized stream. 204 * 205 * @return Description of the Returned Value 206 */ 207 private Object writeReplace() { 208 return new StaticMemberPlaceHolder(ourField); 209 } 210 211 static { 212 UNKNOWN = new ChangeType( 213 "Unknown change", 214 ChangeType.class, 215 "UNKNOWN", 216 null 217 ); 218 } 219 220 /** 221 * Get all ChangeType objects defined within a class. This 222 * includes ChangeTypes defined in superclasses and interfaces. 223 * Only fields declared as public [final] static ChangeType are 224 * returned. 225 * 226 * @param clazz A class to introspect 227 */ 228 229 public static Set getChangeTypes(Class clazz) 230 { 231 Set types = new HashSet(); 232 Field[] fields = clazz.getFields(); 233 for (int i = 0; i < fields.length; ++i) { 234 Field f = fields[i]; 235 if (f.getType().equals(ChangeType.class) && (f.getModifiers() & Modifier.STATIC) != 0) { 236 try { 237 types.add(f.get(null)); 238 } catch (Exception ex) {} 239 } 240 } 241 242 return types; 243 } 244 245 /** 246 * Return the immediate supertype (internal use only) 247 */ 248 249 private ChangeType getSuperType() { 250 return superType; 251 } 252 253 /** 254 * Return an iterator which contains this type, and all supertypes. 255 * 256 * @since 1.2 257 */ 258 259 public Iterator matchingTypes() { 260 return new Iterator() { 261 ChangeType cti = ChangeType.this; 262 263 public boolean hasNext() { 264 return cti != null; 265 } 266 267 public Object next() { 268 if (cti == null) { 269 throw new NoSuchElementException("No more elements"); 270 } 271 272 ChangeType rt = cti; 273 cti = cti.getSuperType(); 274 275 return rt; 276 } 277 278 public void remove() { 279 throw new UnsupportedOperationException("Can't remove"); 280 } 281 } ; 282 } 283 284 /** 285 * Return <code>true</code> iff <code>ct</code> is equal to this type 286 * or any of it's supertypes (including ChangeType.UNKNOWN). If this is 287 * true, then ct is more general than this. 288 * 289 * @since 1.2 290 */ 291 292 public boolean isMatchingType(ChangeType ct) { 293 for (Iterator i = matchingTypes(); i.hasNext(); ) { 294 ChangeType mt = (ChangeType) i.next(); 295 if (mt == ct) { 296 return true; 297 } 298 } 299 300 return false; 301 } 302} 303