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.bio.seq; 022 023import java.util.HashMap; 024import java.util.Iterator; 025import java.util.Map; 026import java.util.NoSuchElementException; 027import java.util.Set; 028 029import org.biojava.bio.Annotatable; 030import org.biojava.bio.Annotation; 031import org.biojava.bio.SimpleAnnotation; 032import org.biojava.utils.AbstractChangeable; 033import org.biojava.utils.ChangeSupport; 034import org.biojava.utils.ChangeType; 035import org.biojava.utils.SmallMap; 036 037/** 038 * Registry of known types of features. 039 * 040 * <p>Historically, the type of a feature within a sequence has been represented 041 * by the class or interface of the feature, or by the type property. Different 042 * databases use different names for types, and the same name can mean different 043 * things depending on where it came from. 044 * </p> 045 * 046 * <p>This class attempts to provide a framework for registering and managing 047 * feature types. It includes the concept of a type having a FeatureFilter 048 * schema that would match all examples of that type, and a URI for that type. 049 * Groups of related types, such as all those defined in embl, can be packaged 050 * up into a single namespace. One type can refer to any number of others, 051 * indicating that they are direct parents. This lets us say things like an 052 * Ensembl exon is a specific type of the general exon type, and also an 053 * instance of the Ensembl feature type.</p> 054 * 055 * <p>All feature types are presumed to have URIs that are of the form: 056 * <code>uri:biojava.org:types:${repository}/${type}</code> where 057 * <code>${repository}</code> is something like embl or ensembl. Feature types 058 * defined within biojava will be in the repository called "core". 059 * <code>${type}</code> is the local type of the feature. This will be something 060 * like exon, or repeat. The total URI must be unique. Different repositories 061 * can (and are encouraged to) define types with the same names.</p> 062 * 063 * @author Matthew Pocock 064 * @since 1.3 065 */ 066public class FeatureTypes { 067 private static final Map repositories; 068 069 /** The standard prefix for all type URIs **/ 070 public static final String URI_PREFIX = "uri:biojava.org:types"; 071 072 static { 073 repositories = new SmallMap(); 074 } 075 076 /** 077 * <p>Fetch a repository by name.</p> 078 * 079 * <p>Use this to find out what types a repository can provide.</p> 080 * 081 * @param name the name of the Repository to fetch 082 * @return the Repository with that name 083 * @throws NoSuchElementException if there is no repository by that name 084 */ 085 public static Repository getRepository(String name) 086 throws NoSuchElementException { 087 Repository rep = (Repository) repositories; 088 089 if(rep == null) { 090 throw new NoSuchElementException("Could not find repository: " + name); 091 } 092 093 return rep; 094 } 095 096 /** 097 * Find the names of all known repositories. 098 * 099 * @return a Set of all repository names 100 */ 101 public static Set getRepositoryNames() { 102 return repositories.keySet(); 103 } 104 105 /** 106 * Add a repository to FeatureTypes. 107 * 108 * @param repos the Repository to add 109 */ 110 public static void addRepository(Repository repos) { 111 repositories.put(repos.getName(), repos); 112 } 113 114 /** 115 * Remove a repository from FeaturTypes. 116 * 117 * @param repos the Repository to remove 118 */ 119 public static void removeRepository(Repository repos) { 120 repositories.remove(repos.getName()); 121 } 122 123 /** 124 * <p>Get a Type by URI.</p> 125 * 126 * <p>This will attemt to resolve the URI to a type by first matching the 127 * standard prefix, then finding a repository of the right name and finally 128 * searching that with the type-name portion of the URI.</p> 129 * 130 * @param uri the URI to resolve 131 * @return a Type with that URI 132 * @throws NoSuchElementException if the type could not be resolved 133 */ 134 public static Type getType(String uri) { 135 if(!uri.startsWith(URI_PREFIX)) { 136 throw new NoSuchElementException( 137 "All types start with: " + URI_PREFIX + 138 " while processing " + uri 139 ); 140 } 141 142 String names = uri.substring(URI_PREFIX.length() + 1); 143 int slash = uri.indexOf("/"); 144 String repName = names.substring(0, slash); 145 String typeName = names.substring(slash + 1); 146 147 Repository rep = getRepository(repName); 148 return rep.getType(typeName); 149 } 150 151 /** 152 * <p>Work out if one type is a sub-type of another.</p> 153 * 154 * <p>This is the transiative closure of Type.getParent(). It will return true 155 * if superType is reachable by following getParent() calls, and false 156 * otherwise.</p> 157 * 158 * @param subType the Type of the potential sub type 159 * @param superType the Type of the potential super type 160 * @return true if they are sub-super types, false otherwise 161 */ 162 public static boolean isSubTypeOf(Type subType, Type superType) { 163 Set parents = subType.getParents(); 164 165 if(parents.contains(superType.getURI())) { 166 return true; 167 } 168 169 for(Iterator i = parents.iterator(); i.hasNext(); ) { 170 String puri = (String) i.next(); 171 return isSubTypeOf(getType(puri), superType); 172 } 173 174 return false; 175 } 176 177 /** 178 * A named collection of Types. 179 * 180 * @author Matthew Pocock 181 * @since 1.3 182 */ 183 public static interface Repository 184 extends Annotatable { 185 /** 186 * <p>The name of this repository.</p> 187 * 188 * <p>This will be the ${repository} component of any URIs of types defined 189 * here.</p> 190 * 191 * @return the name of the repository 192 */ 193 String getName(); 194 195 /** 196 * Get a set of all type names defined in this repository. 197 * 198 * @return a Set of Type names as Strings 199 */ 200 Set getTypes(); 201 202 /** 203 * Find the type for a name. 204 * 205 * @param name the name of the Type 206 * @return the Type of that name 207 * @throws NoSuchElementException if that type can not be found 208 */ 209 Type getType(String name) 210 throws NoSuchElementException; 211 } 212 213 /** 214 * A type of feature. 215 * 216 * @author Matthew Pocock 217 * @since 1.3 218 */ 219 public static interface Type 220 extends Annotatable { 221 /** 222 * <p>Get the schema for this type.</p> 223 * 224 * <p>The schema is represented as a FeatureFilter. This will almost 225 * certainly be a complext filter using ands and ors to combine multiple 226 * constraints. A particular type may chose to restrict any one of the 227 * feature's properties, their allowed children and their allowed parents 228 * in a feature hierachy, the type of the annotation associated with it and 229 * anything else that can be expressed using a feature fitler.</p> 230 * 231 * <p>For a feature to actualy conform to this type, it must be acceptable 232 * by the schema filter.</p> 233 * 234 * @return the schema FeatureFilter 235 */ 236 FeatureFilter getSchema(); 237 238 /** 239 * Get the name of this type. 240 * 241 * @return the Type name 242 */ 243 String getName(); 244 245 /** 246 * Get a set of URIs for parent types. 247 * 248 * @return a Set of all parent URIs 249 */ 250 Set getParents(); 251 252 /** 253 * <p>Get the URI for this type.</p> 254 * 255 * <p>The URI will be composed according to the rules defined in 256 * FeatureTypes, being of the form 257 * <code>uri:biojava.org:types:${repository}/${type}</code>.</p> 258 * 259 * @return the URI for this type 260 */ 261 String getURI(); 262 } 263 264 /** 265 * A simple implementation of a Repository. 266 * 267 * @author Matthew Pocock 268 * @since 1.3 269 */ 270 public static class RepositoryImpl 271 extends AbstractChangeable 272 implements Repository { 273 private final String name; 274 private final Map types; 275 private final Annotation ann; 276 277 /** 278 * Create a named repository. 279 * 280 * @param name the name of this repository 281 */ 282 public RepositoryImpl(String name) { 283 this.name = name; 284 types = new HashMap(); 285 this.ann = new SimpleAnnotation(); 286 } 287 288 protected ChangeSupport getChangeSupport(ChangeType ct) { 289 ChangeSupport cs = super.getChangeSupport(ct); 290 ann.addChangeListener(new Annotatable.AnnotationForwarder(this, cs), Annotation.PROPERTY); 291 return cs; 292 } 293 294 public Annotation getAnnotation() { 295 return ann; 296 } 297 298 public String getName() { 299 return name; 300 } 301 302 public Set getTypes() { 303 return types.keySet(); 304 } 305 306 public Type getType(String name) 307 throws NoSuchElementException { 308 Type type = (Type) types.get(name); 309 if(type == null) { 310 throw new NoSuchElementException( 311 "Could not find type " + name + 312 " in repository " + getName() 313 ); 314 } 315 return type; 316 } 317 318 /** 319 * Create a new type in this repository. 320 * 321 * @param name the Type name 322 * @param schema the FeatureFilter defining the type 323 * @param parents the Set (possibly empty) of parent URIs 324 */ 325 public Type createType( 326 String name, 327 FeatureFilter schema, 328 Set parents 329 ) { 330 Type type = new TypeImpl(name, schema, parents); 331 types.put(name, type); 332 return type; 333 } 334 335 private class TypeImpl 336 extends AbstractChangeable 337 implements Type { 338 private Annotation ann; 339 private String name; 340 private FeatureFilter schema; 341 private Set parents; 342 343 public TypeImpl( 344 String name, 345 FeatureFilter schema, 346 Set parents 347 ) { 348 this.name = name; 349 this.schema = schema; 350 this.parents = parents; 351 this.ann = new SimpleAnnotation(); 352 } 353 354 protected ChangeSupport getChangeSupport(ChangeType ct) { 355 ChangeSupport cs = super.getChangeSupport(ct); 356 ann.addChangeListener(new Annotatable.AnnotationForwarder(this, cs), Annotation.PROPERTY); 357 return cs; 358 } 359 360 public Annotation getAnnotation() { 361 return ann; 362 } 363 364 public String getName() { 365 return name; 366 } 367 368 public FeatureFilter getSchema() { 369 return schema; 370 } 371 372 public Set getParents() { 373 return parents; 374 } 375 376 public String getURI() { 377 return URI_PREFIX + "/" + RepositoryImpl.this.getName() + "/" + name; 378 } 379 } 380 } 381}