001package org.biojava.bio.seq.impl;
002
003import java.lang.reflect.Field;
004import java.lang.reflect.Method;
005
006import org.biojava.bio.BioError;
007import org.biojava.bio.BioException;
008import org.biojava.bio.seq.Feature;
009import org.biojava.utils.AssertionFailure;
010
011/**
012 * Common things you may want to do with feature templates.
013 *
014 * @author Matthew Pocock
015 * @since 1.4
016 */
017public final class TemplateUtils {
018  // no instances of this class
019  private TemplateUtils() {}
020
021  /**
022   * This attempts to divine the 'best' template class for a feature and return
023   * a new instance readly for pupulating.
024   *
025   * <p>
026   * This will (hopefully) be the most derived feature interface implemented by
027   * a feature class. This code assumes that feature interfaces are singly
028   * inherited. Of course, with the current template system, it is a fairly safe
029   * assumption.
030   * </p>
031   *
032   *
033   */
034  public static Feature.Template instantiateTemplate(Feature feat)
035  throws BioException {
036    Feature.Template tmpl = searchForTemplate(feat.getClass());
037    if(tmpl == null) {
038      throw new AssertionFailure(
039              "Could not find template for feature class: " + feat.getClass());
040    }
041    return tmpl;
042  }
043
044  private static Feature.Template searchForTemplate(Class fClass)
045  throws BioException {
046    if(fClass.isInterface()) {
047      if(Feature.class.isAssignableFrom(fClass)) {
048        return instantiateTemplate(fClass);
049      }
050    }
051
052    Class[] interfaces = fClass.getInterfaces();
053    for(int i = 0; i < interfaces.length; i++) {
054      Feature.Template tmpl = searchForTemplate(interfaces[i]);
055      if(tmpl != null) {
056        return tmpl;
057      }
058    }
059
060    Class parent = fClass.getSuperclass();
061    if(parent != null) {
062      return searchForTemplate(parent);
063    }
064
065    return null;
066  }
067
068  private static Feature.Template instantiateTemplate(Class fClass)
069  throws BioException{
070    // let's assume this has a *.Template class & instantiate that
071    Class[] declClasses = fClass.getDeclaredClasses();
072    for(int i = 0; i < declClasses.length; i++) {
073      if(declClasses[i].getName().equals(fClass.getName() + "$" + "Template")) {
074        try {
075          return (Feature.Template) declClasses[i].newInstance();
076        } catch (IllegalAccessException iae) {
077          throw new BioException(
078                  "Expected template no-args constructor to be accessible",
079                  iae);
080        } catch (InstantiationException ie) {
081          throw new BioException(
082                  "Failed to execute no-args constructor",
083                  ie);
084        }
085      }
086    }
087
088    return null;
089  }
090
091  /**
092   * This attempts to populate the fields of a template using
093   * the publically accessible information in a feature. It is simple
094   * to call populate() within Feature.makeTemplate() to ensure all the
095   * slots get filled.
096   *
097   * @param templ the Feature.Template instance to populate
098   * @param feat  the Feature to read info from
099   */
100  public static void populate(Feature.Template templ, Feature feat)
101  throws BioException {
102    Field[] fields = templ.getClass().getFields();
103    Method[] methods = feat.getClass().getMethods();
104
105    for(int i = 0; i < fields.length; i++) {
106      Field field = fields[i];
107      String fName = field.getName();
108      String methName =
109        "get" +
110        fName.substring(0, 1).toUpperCase() +
111        fName.substring(1);
112
113      Method method = null;
114      for(int j = 0; j < methods.length; j++) {
115        Method meth = methods[j];
116        if(methods[j].getName().equals(methName)) {
117          method = meth;
118        }
119      }
120      if(method == null) {
121        throw new BioException("Expecting to find a method named: " + methName);
122      }
123
124      try {
125        field.set(templ, method.invoke(feat, new Object[] {}));
126      } catch (Exception e) {
127        throw new BioError("Couldn't access template fields", e);
128      }
129    }
130  }
131
132  public static Feature.Template makeTemplate(Feature feat)
133  throws BioException {
134    Feature.Template templ = instantiateTemplate(feat);
135    populate(templ, feat);
136    return templ;
137  }
138}