package uk.ac.ebi.fg.myequivalents.managers.interfaces; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; import com.google.common.base.Function; import uk.ac.ebi.fg.myequivalents.model.Entity; import uk.ac.ebi.fg.myequivalents.model.EntityMapping; import uk.ac.ebi.fg.myequivalents.model.Repository; import uk.ac.ebi.fg.myequivalents.model.Service; import uk.ac.ebi.fg.myequivalents.model.ServiceCollection; import uk.ac.ebi.utils.collections.MapCollection; /** * * This is the class used to format the responses returned by the {@link EntityMappingManager}. For instance, the REST-based * web service uses this class to format its output in XML (thanks to JAXB mappings). * * <dl><dt>date</dt><dd>Jun 11, 2012</dd></dl> * @author Marco Brandizi * */ @XmlRootElement ( name = "mappings" ) @XmlAccessorType ( XmlAccessType.NONE ) @XmlType ( name = "", propOrder = { "services", "repositories", "serviceCollections", "bundles" } ) public class EntityMappingSearchResult { /** * This represents a set of entities that cross-reference each-other, it's the thing returned by * {@link EntityMappingSearchResult#getBundles()}. Having this class in between is a bit cumbersome, * but required for proper mapping via JAXB. * * <dl><dt>date</dt><dd>Jul 18, 2012</dd></dl> * @author Marco Brandizi * */ @XmlRootElement ( name = "bundle" ) @XmlAccessorType ( XmlAccessType.NONE ) public static class Bundle { private Bundle () { } private Set<Entity> entities = new HashSet<Entity> (); private void addEntity ( Entity entity ) { this.entities.add ( entity ); } @XmlElement ( name = "entity", type = ExposedEntity.class ) public Set<Entity> getEntities () { return entities; } protected void setEntities ( Set<Entity> entities ) { this.entities = entities; } } private final boolean wantRawResult; private Set<Service> services; private Set<ServiceCollection> serviceCollections; private Set<Repository> repositories; private final Map<String, Bundle> bundles = new HashMap<String, Bundle> (); /** * This is used in {@link #getBundles()}, because JAXB (damn it!) calls its add() method when it has to unmarshal * from XML. */ private final MapCollection<String, Bundle> bundlesCollection = new MapCollection<> ( bundles, new Function<Bundle, String>() { @Override public String apply ( Bundle input ) { // Yes, it's undetermined, but we don't know any other way and bundles don't change after insertion Entity e = input.getEntities ().iterator ().next (); return e.getServiceName () + ':' + e.getAccession (); } }); EntityMappingSearchResult () { this ( false ); } /** * @param wantRawResult if true, only bundles will be stored into this object. See * {@link DbEntityMappingManager#getMappings(boolean, String...)}. * * Usually you don't want to instantiate this yourself, you should leave it to the {@link EntityMappingManager} you're * using. */ public EntityMappingSearchResult ( boolean wantRawResult ) { super (); this.wantRawResult = wantRawResult; if ( wantRawResult ) { services = null; serviceCollections = null; repositories = null; } else { services = new HashSet<Service> (); serviceCollections = new HashSet<ServiceCollection> (); repositories = new HashSet<Repository> (); } } @XmlElementWrapper ( name = "services" ) @XmlElement ( name = "service", type = ExposedService.class ) public Set<Service> getServices () { return services; } /** Needed by JAXB for un-marshalling */ protected void setServices ( Set<Service> services ) { this.services = services; } @XmlElementWrapper ( name = "service-collections" ) @XmlElement ( name = "service-collection" ) public Set<ServiceCollection> getServiceCollections () { return serviceCollections; } /** Needed by JAXB for un-marshalling */ protected void setServiceCollections ( Set<ServiceCollection> serviceCollections ) { this.serviceCollections = serviceCollections; } @XmlElementWrapper ( name = "repositories" ) @XmlElement ( name = "repository" ) public Set<Repository> getRepositories () { return repositories; } /** Needed by JAXB for unmarshalling */ protected void setRepositories ( Set<Repository> repositories ) { this.repositories = repositories; } @XmlElementWrapper ( name = "bundles" ) @XmlElement ( name = "bundle" ) public Collection<Bundle> getBundles () { return this.bundlesCollection; } /** Needed by JAXB for un-marshalling */ @SuppressWarnings ( "rawtypes" ) protected void setBundles ( Collection<Bundle> bundles ) { if ( bundles instanceof MapCollection && ((MapCollection) bundles).getBase () == this.bundles ) // OK, it's the damn JAXB calling us again, after having initialised it all, using bundlesCollection.add() and // ignoring me the setter. That's idiotic, but we need to go around it return; this.bundles.clear (); for ( Bundle bundle: bundles ) this.bundlesCollection.add ( bundle ); } /** * This adds the entity to the proper {@link Bundle} and, if not in raw-result mode, adds related {@link Service}s, * {@link ServiceCollection}s and {@link Repository repositories} to this result. * */ public void addEntityMapping ( EntityMapping em ) { String bundleId = em.getBundle (); Bundle bundle = bundles.get ( em.getBundle () ); if ( bundle == null ) { bundles.put ( bundleId, bundle = new Bundle () ); } bundle.addEntity ( em.getEntity () ); Service service = em.getService (); // Cause unmarshalling anyway if ( wantRawResult ) return; services.add ( service ); ServiceCollection serviceCollection = service.getServiceCollection (); if ( serviceCollection != null ) serviceCollections.add ( serviceCollection ); Repository repo = service.getRepository (); if ( repo != null ) repositories.add ( repo ); } /** * This is just a wrapper of {@link #addEntityMapping(EntityMapping)}. * */ public void addAllEntityMappings ( Collection<EntityMapping> mappings ) { for ( EntityMapping em: mappings ) addEntityMapping ( em ); } @Override public String toString () { StringBuilder sb = new StringBuilder ( "EntityMappingResult {\n" ); if ( !wantRawResult ) { sb.append ( " services: {\n" ); for ( Service service: services ) sb.append ( " " ).append ( service.toString () + "\n"); sb.append ( " }\n" ); sb.append ( " repositories: {\n" ); for ( Repository repo: repositories ) sb.append ( " " ).append ( repo.toString () ); sb.append ( " }\n" ); sb.append ( " service-collections: {\n" ); for ( ServiceCollection sc: serviceCollections ) sb.append ( " " ).append ( sc.toString () ); sb.append ( " }\n" ); } sb.append ( " bundles: {\n" ); for ( Bundle bundle: this.getBundles () ) { sb.append ( " {\n" ); for ( Entity entity: bundle.getEntities () ) sb.append ( " " + entity.toString () + "\n" ); sb.append ( " }\n" ); } sb.append ( " }\n" ); return sb.toString (); } }