gt:         SavedBag savedBag = storeSavedBag();
context: package org.intermine.api.profile;
  
  /*
   * Copyright (C) 2002-2014 FlyMine
   *
   * This code may be freely distributed and modified under the
   * terms of the GNU Lesser General Public Licence.  This should
   * be distributed with the code.  See the LICENSE file for more
   * information or http://www.gnu.org/copyleft/lesser.html.
   *
   */
  
  import java.io.IOException;
  import java.io.InputStream;
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.Collections;
  import java.util.Date;
  import java.util.List;
  import java.util.Properties;
  import java.util.Set;
  
  import org.apache.commons.lang.StringUtils;
  import org.apache.log4j.Logger;
  import org.intermine.api.bag.ClassKeysNotFoundException;
  import org.intermine.api.bag.IncompatibleTypesException;
  import org.intermine.api.bag.UnknownBagTypeException;
  import org.intermine.api.search.PropertyChangeEvent;
  import org.intermine.api.search.WebSearchable;
  import org.intermine.metadata.ClassDescriptor;
  import org.intermine.metadata.FieldDescriptor;
  import org.intermine.metadata.Model;
  import org.intermine.metadata.ReferenceDescriptor;
  import org.intermine.model.InterMineObject;
  import org.intermine.model.userprofile.SavedBag;
  import org.intermine.objectstore.ObjectStore;
  import org.intermine.objectstore.ObjectStoreException;
  import org.intermine.objectstore.ObjectStoreWriter;
  import org.intermine.objectstore.query.BagConstraint;
  import org.intermine.objectstore.query.Constraint;
  import org.intermine.metadata.ConstraintOp;
  import org.intermine.objectstore.query.ConstraintSet;
  import org.intermine.objectstore.query.ObjectStoreBag;
  import org.intermine.objectstore.query.Query;
  import org.intermine.objectstore.query.QueryClass;
  import org.intermine.objectstore.query.QueryField;
  import org.intermine.objectstore.query.QueryFunction;
  import org.intermine.objectstore.query.QueryObjectPathExpression;
  import org.intermine.objectstore.query.Results;
  import org.intermine.objectstore.query.ResultsRow;
  import org.intermine.objectstore.query.SimpleConstraint;
  import org.intermine.objectstore.query.SingletonResults;
  import org.intermine.metadata.TypeUtil;
  
  /**
   * An object that represents a bag of objects in our database for the webapp. It is backed by an
   * ObjectStoreBag object, but contains extra data such as name and description.
   *
   * @author Kim Rutherford
   * @author Matthew Wakeling
   * @author Daniela Butano
   */
  public class InterMineBag extends StorableBag implements WebSearchable, Cloneable
  {
      protected static final Logger LOG = Logger.getLogger(InterMineBag.class);
      /** name of bag values table */
      public static final String BAG_VALUES = "bagvalues";
      private String name;
      private String type;
      private String description;
      private Date dateCreated;
      private List<String> keyFieldNames = new ArrayList<String>();
      private BagState state;
      private ObjectStoreBag osb;
      private ObjectStore os;
      private ObjectStoreWriter uosw;
  
      private Set<ClassDescriptor> classDescriptors;
  
      /**
       * Constructs a new InterMineIdBag, and saves it in the UserProfile database.
       *
       * @param name the name of the bag
       * @param type the class of objects stored in the bag
       * @param description the description of the bag
       * @param dateCreated the Date when this bag was created
       * @param state the state of the bag
       * @param os the production ObjectStore
       * @param profileId the ID of the user in the userprofile database
       * @param uosw the ObjectStoreWriter of the userprofile database
       * @param keyFieldNames the list of identifiers defined for this bag
       * @throws UnknownBagTypeException if the type bag is unknown
       * @throws ObjectStoreException if an error occurs
       */
      public InterMineBag(String name, String type, String description, Date dateCreated,
          BagState state, ObjectStore os, Integer profileId, ObjectStoreWriter uosw,
          List<String> keyFieldNames)
          throws UnknownBagTypeException, ClassKeysNotFoundException, ObjectStoreException {
          this.type = TypeUtil.unqualifiedName(type);
          init(name, description, dateCreated, state, os, profileId, uosw);
          this.keyFieldNames = keyFieldNames;
      }
  
      /**
       * Constructs a new InterMineBag, and saves it in the UserProfile database.
       *
       * @param name the name of the bag
       * @param type the class of objects stored in the bag
       * @param description the description of the bag
       * @param dateCreated the Date when this bag was created
       * @param state the state of the bag
       * @param os the production ObjectStore
       * @param uosw the ObjectStoreWriter of the userprofile database
       * @param keyFieldNames the list of identifiers defined for this bag
       * @throws UnknownBagTypeException if the type bag is unknown
       * @throws ObjectStoreException if an error occurs
       */
      public InterMineBag(String name, String type, String description, Date dateCreated,
          BagState state, ObjectStore os, ObjectStoreWriter uosw,
          List<String> keyFieldNames)
          throws UnknownBagTypeException, ClassKeysNotFoundException, ObjectStoreException {
          this(name, type, description, dateCreated, state, os, null, uosw, keyFieldNames);
      }
  
      private void init(String name, String description, Date dateCreated, BagState state,
          ObjectStore os, Integer profileId, ObjectStoreWriter uosw)
          throws UnknownBagTypeException, ObjectStoreException {
          checkAndSetName(name);
          this.description = description;
          this.dateCreated = dateCreated;
          this.state = state;
          this.os = os;
          this.profileId = profileId;
          this.osb = os.createObjectStoreBag();
          this.uosw = uosw;
          this.savedBagId = null;
          SavedBag savedBag = storeSavedBag();
          this.savedBagId = savedBag.getId();
          setClassDescriptors();
      }
  
      /**
       * Loads an InterMineBag from the UserProfile database.
       *
       * @param os the production ObjectStore
       * @param savedBagId the ID of the bag in the userprofile database
       * @param uosw the ObjectStoreWriter of the userprofile database
       * @throws UnknownBagTypeException if the type bag is unknown
       * @throws ObjectStoreException if something goes wrong
       */
      public InterMineBag(ObjectStore os, Integer savedBagId, ObjectStoreWriter uosw)
          throws UnknownBagTypeException, ObjectStoreException {
          this(os, savedBagId, uosw, true);
      }
  
      /**
       * Loads an InterMineBag from the UserProfile database.
       *
       * @param os the production ObjectStore
       * @param savedBagId the ID of the bag in the userprofile database
       * @param uosw the ObjectStoreWriter of the userprofile database
       * @param classDescriptor if true the classDescriptor will be set
       * @throws UnknownBagTypeException if the type bag is unknown
       * @throws ObjectStoreException if something goes wrong
       */
      public InterMineBag(ObjectStore os, Integer savedBagId, ObjectStoreWriter uosw,
          boolean classDescriptor)
          throws UnknownBagTypeException, ObjectStoreException {
          this.os = os;
          this.uosw = uosw;
          this.savedBagId = savedBagId;
          ObjectStore uos = uosw.getObjectStore();
          SavedBag savedBag = (SavedBag) uos.getObjectById(savedBagId, SavedBag.class);
          checkAndSetName(savedBag.getName());
          this.type = TypeUtil.unqualifiedName(savedBag.getType());
          this.description = savedBag.getDescription();
          this.dateCreated = savedBag.getDateCreated();
          this.profileId = savedBag.proxGetUserProfile().getId();
          setState(savedBag.getState());
          this.osb = new ObjectStoreBag(savedBag.getOsbId());
          if (classDescriptor) {
              setClassDescriptors();
          }
      }
  
  
  
      /**
       * Declare that this bag is invalid, and return its InvalidBag representation.
       * @return An InvalidBag version of the database record.
       */
      protected InvalidBag invalidate() {
          try {
              return new InvalidBag(name, type, description, dateCreated, os,
                  savedBagId, profileId, osb, uosw);
          } catch (ObjectStoreException e) {
              // Shouldn't happen, unless this.osb == null, and then things are really screwy.
              throw new RuntimeException("Unexpected error:", e);
          }
      }
  
      private void setClassDescriptors() throws UnknownBagTypeException {
          try {
              Class<?> cls = Class.forName(getQualifiedType());
              classDescriptors = os.getModel().getClassDescriptorsForClass(cls);
          } catch (ClassNotFoundException e) {
              throw new UnknownBagTypeException("bag type " + getQualifiedType() + " not known", e);
          }
      }
  
      private void setState(String savedBagStatus) {
          if (BagState.CURRENT.toString().equals(savedBagStatus)) {
              state = BagState.CURRENT;
          } else if (BagState.NOT_CURRENT.toString().equals(savedBagStatus)) {
              state = BagState.NOT_CURRENT;
          } else if (BagState.UPGRADING.toString().equals(savedBagStatus)) {
              state = BagState.UPGRADING;
          } else {
              state = BagState.TO_UPGRADE;
          }
      }
  
      /**
       * Delete this bag from the userprofile database, bag should not be used after this method has
       * been called. Delete the ids from the production database too.
       * @throws ObjectStoreException if problem deleting bag
       */
      @Override
      public void delete() throws ObjectStoreException {
          super.delete();
          if (profileId != null) {
              SavedBag savedBag = (SavedBag) uosw.getObjectStore().getObjectById(savedBagId,
                      SavedBag.class);
              uosw.delete(savedBag);
              removeIdsFromBag(getContentsAsIds(), false);
              deleteAllBagValues();
              this.profileId = null;
              this.savedBagId = null;
          }
      }
  
      /**
       * Returns a List which contains the contents of this bag as Integer IDs.
       *
       * @return a List of Integers
       */
      @SuppressWarnings({ "unchecked", "rawtypes" })
      public List<Integer> getContentsAsIds() {
          Query q = new Query();
          q.addToSelect(osb);
          q.setDistinct(false);
          SingletonResults res = os.executeSingleton(q, 1000, false, true, true);
          return ((List) res);
      }
  
      /**
       * Returns a List which contains the ids given in input and contained
       * in this bag as Integer IDs.
       * @param ids the list of ids
       * @return a List of Integers
       */
      @SuppressWarnings({ "unchecked", "rawtypes" })
      public List<Integer> getIdsContained(Collection<Integer> ids) {
          Query q = new Query();
          q.setDistinct(false);
          try {
              Class<? extends InterMineObject> clazz =
                  (Class<InterMineObject>) Class.forName(getQualifiedType());
              QueryClass qc = new QueryClass(clazz);
              QueryField idField = new QueryField(qc, "id");
              q.addToSelect(idField);
              q.addFrom(qc);
  
              BagConstraint constraint1 = new BagConstraint(idField, ConstraintOp.IN, ids);
              BagConstraint constraint2 = new BagConstraint(idField, ConstraintOp.IN, osb);
              ConstraintSet constraintSet = new ConstraintSet(ConstraintOp.AND);
              constraintSet.addConstraint(constraint1);
              constraintSet.addConstraint(constraint2);
              q.setConstraint(constraintSet);
  
          } catch (ClassNotFoundException nfe) {
              LOG.error("Error retriving class for bag: " + name, nfe);
          }
  
          SingletonResults res = os.executeSingleton(q, 1000, false, true, true);
          return ((List) res);
      }
  
      /**
       * Returns a List of BagValue (key field value and extra value) of the objects contained
       * by this bag.
       * @return the list of BagValue
       */
      @Override
      public List<BagValue> getContents() {
          if (isCurrent()) {
              return getContentsFromOsb();
          } else {
              return super.getContents();
          }
      }
  
      /**
       * Returns the values of the key field objects and extra attribute (if it exists)
       * contained in the bag. The values are retrieved using the objectstorebag.
       * @param ids the collection of id
       * @return the list of values
       */
      private List<BagValue> getContentsFromOsb() {
          return getContentsFromOsb(null);
      }
  
      /**
       * Returns the values of the key field objects and extra attribute (if it exists) having the id
       * specified in input and contained in the bag.The values are retrieved using the objectstorebag
       * @param ids the collection of id
       * @return the list of values
       */
      private List<BagValue> getContentsFromOsb(Collection<Integer> ids) {
          List<BagValue> keyFieldValueList = new ArrayList<BagValue>();
          Properties bagProperties = new Properties();
          InputStream isBag = getClass().getClassLoader().getResourceAsStream("extraBag.properties");
          String extraClassName = null;
          String extraConnectField = null;
          String extraConstrainField = null;
          if (isBag != null) {
              try {
                  bagProperties.load(isBag);
                  extraConnectField = bagProperties.getProperty("extraBag.connectField");
                  extraClassName = bagProperties.getProperty("extraBag.className");
                  extraConstrainField = bagProperties.getProperty("extraBag.constrainField");
              } catch (IOException ioe) {
                  ioe.printStackTrace();
                  LOG.error("Problems loading extraBag.properties. ", ioe);
              }
          } else {
              LOG.error("Could not find extraBag.properties file");
          }
          boolean hasExtraValue = false;
          if (extraClassName != null) {
              for (ClassDescriptor cd : classDescriptors) {
                  FieldDescriptor fd = cd.getFieldDescriptorByName(extraConnectField);
                  if (fd != null && fd instanceof ReferenceDescriptor) {
                      hasExtraValue = true;
                      break;
                  }
              }
          }
          Query q = new Query();
          q.setDistinct(false);
          try {
              QueryClass qc = new QueryClass(Class.forName(getQualifiedType()));
              q.addFrom(qc);
              if (hasExtraValue) {
                  QueryObjectPathExpression qope = new QueryObjectPathExpression(qc,
                                                   extraConnectField);
                  qope.addToSelect(qope.getDefaultClass());
                  q.addToSelect(qope);
                  q.addToSelect(qc);
              }
              if (keyFieldNames.isEmpty()) {
                  return Collections.EMPTY_LIST;
              }
              for (String keyFieldName : keyFieldNames) {
                  q.addToSelect(new QueryField(qc, keyFieldName));
              }
              QueryField idField = new QueryField(qc, "id");
              BagConstraint constraint;
              if (ids == null || ids.isEmpty()) {
                  constraint = new BagConstraint(idField, ConstraintOp.IN, osb);
              } else {
                  constraint = new BagConstraint(idField, ConstraintOp.IN, ids);
              }
              q.setConstraint(constraint);
              Results res = os.execute(q);
              for (Object rowObj : res) {
                  ResultsRow<?> row = (ResultsRow<?>) rowObj;
                  String value;
                  String extra = "";
                  int index = 0;
                  if (hasExtraValue) {
                      InterMineObject imObj = (InterMineObject) row.get(0);
                      if (imObj != null) {
                          extra = (String) imObj.getFieldValue(extraConstrainField);
                      } else {
                          extra = "";
                      }
                      index = index + 2; //row.get(1) contains the class of the bag type
                  }
                  for (; index < row.size(); index++) {
                      value = (String) row.get(index);
                      if (value != null && !"".equals(value)) {
                          keyFieldValueList.add(new BagValue(value, extra));
                          break;
                      }
                  }
              }
              return keyFieldValueList;
          } catch (ClassNotFoundException cne) {
              return new ArrayList<BagValue>();
          } catch (IllegalAccessException iae) {
              return new ArrayList<BagValue>();
          }
      }
  
      /**
       * Upgrades the ObjectStoreBag with a new ObjectStoreBag containing the collection of elements
       * given in input
       * @param values the collection of elements to add
       * @param updateBagValues id true if we upgrade the bagvalues table
       * @throws ObjectStoreException if an error occurs fetching a new ID
       */
      public void upgradeOsb(Collection<Integer> values, boolean updateBagValues)
          throws ObjectStoreException {
          ObjectStoreWriter oswProduction = null;
          SavedBag savedBag = (SavedBag) uosw.getObjectById(savedBagId, SavedBag.class);
          try {
              oswProduction = os.getNewWriter();
              osb = oswProduction.createObjectStoreBag();
              oswProduction.addAllToBag(osb, values);
              savedBag.setOsbId(osb.getBagId());
              savedBag.setState(BagState.CURRENT.toString());
              state = BagState.CURRENT;
              uosw.store(savedBag);
              if (updateBagValues) {
                  updateBagValues();
              }
          } finally {
              if (oswProduction != null) {
                  oswProduction.close();
              }
          }
      }
  
  
      @Override
      public int getSize() throws ObjectStoreException {
          Query q = new Query();
          q.addToSelect(osb);
          q.setDistinct(false);
          return os.count(q, ObjectStore.SEQUENCE_IGNORE);
      }
  
      /** @return the user-profile object store writer **/
      public ObjectStoreWriter getUserProfileWriter() {
          return uosw;
      }
  
      /**
       * Returns the ObjectStoreBag, so that elements can be added and removed.
       *
       * @return the ObjectStoreBag
       */
      public ObjectStoreBag getOsb() {
          return osb;
      }
  
      /**
       * Sets the ObjectStoreBag.
       *
       * @param osb the ObjectStoreBag
       */
      public void setOsb(ObjectStoreBag osb) {
          this.osb = osb;
      }
  
      /**
       * Sets the profileId - moves this bag from one profile to another.
       *
       * @param profileId the ID of the new userprofile
       * @throws ObjectStoreException if something goes wrong
       */
      public void setProfileId(Integer profileId)
          throws ObjectStoreException {
          this.profileId = profileId;
  