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}