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