Changeset 7296


Ignore:
Timestamp:
Feb 20, 2017, 1:04:19 PM (7 years ago)
Author:
Nicklas Nordborg
Message:

References #2056: Improve API for project-specific annotations

Introduced methods on the AnnotationSet class that can be used to create/read/query/delete project-specific or default annotation values no matter what the currently active project is.

The test class has been extended with additional test cases.

Location:
trunk/src
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/core/net/sf/basedb/core/Annotation.java

    r7262 r7296  
    311311  }
    312312 
     313  private boolean ignoreActiveProject;
     314  /**
     315    Set the project that this annotation value should
     316    belong to. This will disable the automatic check
     317    that creates a new project-specific value when
     318    trying to change the default value and a project
     319    is active.
     320    @since 3.11
     321  */
     322  void setProjectId(int projectId)
     323  {
     324    getData().setProjectId(projectId);
     325    ignoreActiveProject = true;
     326  }
     327 
    313328  /**
    314329    Get the annotation set this annotation belongs to.
     
    394409  {
    395410    return getData().getProjectId() != 0;
     411  }
     412 
     413  /**
     414    Checks if this annotation is ignoring the currently active
     415    project. This affects how the {@link #setValuesIfDifferent(List, Unit)}
     416    methods treats annotations that represents the default value
     417    for a project-specific annotation.
     418
     419    If this method returns FALSE the default value is not modified.
     420    Instead, a new project-specific value for the currently active project
     421    is created (or the existing one is updated)
     422
     423    If this method returns TRUE it is always this annotation that is
     424    modified.
     425   
     426    @return TRUE if ignoring the active project
     427    @since 3.11
     428  */
     429  public boolean isIgnoringActiveProject()
     430  {
     431    return ignoreActiveProject;
    396432  }
    397433 
     
    706742    changed. The project-specific annotation can be retrieved by calling
    707743    {@link AnnotationSet#getAnnotation(AnnotationType)}.
     744    <p>
     745    An exception to the above (since BASE 3.11) is if the annotation was loaded with
     746    {@link AnnotationSet#getProjectAnnotation(AnnotationType, int)} in
     747    which case currently active project is ignored and the values are
     748    always changed on this annotation (eg. the same as if this annotation
     749    was not using project-specific annotations).
     750    Use the {@link #isIgnoringActiveProject()} to find out if the active
     751    project is ignored or not.
    708752   
    709753    @param values A list containing the new values
     
    736780   
    737781    int projectId = getSessionControl().getActiveProjectId();
    738     boolean useProjectAnnotations = isInDatabase() && annotationType.getProjectAnnotations() &&
    739       projectId != 0 && getData().getProjectId() == 0;
     782    boolean useProjectAnnotations = !ignoreActiveProject && isInDatabase() &&
     783      annotationType.getProjectAnnotations() && projectId != 0 && getData().getProjectId() == 0;
    740784    if (useProjectAnnotations)
    741785    {
  • trunk/src/core/net/sf/basedb/core/AnnotationSet.java

    r7261 r7296  
    602602 
    603603  /**
     604    Get a value for a project-specific annotation using the specified project id instead
     605    of the currently active project. Note that this method behaves differently than
     606    the {@link #getAnnotation(AnnotationType)} method.
     607   
     608     * If the annotation type is not using project annotation, the project id is ignored
     609     * If projectId=0 the default annotation  value is returned (new or existing)
     610     * If projectId!=0 a project-specific annotation value is returned (new or existing,
     611       the default value is never returned)
     612       
     613    The returned annotation is always locked to the project it has been defined for.
     614    Calling {@link Annotation#setValuesIfDifferent(List, Unit)} will always modify the
     615    existing value (it will not create a new project-specific value when trying
     616    to change the default value).
     617
     618    @param annotationType The annotation type
     619    @param projectId The project id or 0 to get the default value
     620    @since 3.11
     621  */
     622  public Annotation getProjectAnnotation(AnnotationType annotationType, int projectId)
     623  {
     624    if (annotationType == null) throw new InvalidUseOfNullException("annotationType");
     625    DbControl dc = getDbControl();
     626   
     627    int primary = Annotation.Source.PRIMARY.ordinal();
     628    if (!annotationType.getProjectAnnotations()) projectId = 0;
     629
     630    // Search for an existing primary annotation
     631    AnnotationTypeData atd = annotationType.getData();
     632    AnnotationData found = null;
     633    for (AnnotationData ad : getData().getAnnotations())
     634    {
     635      if (ad.getSource() == primary && ad.getAnnotationType().equals(atd))
     636      {
     637        // This is a primary annotation of the correct type
     638        // Check for project-specific match
     639        if (ad.getProjectId() == projectId)
     640        {
     641          found = ad;
     642          break;
     643        }
     644      }
     645    }
     646   
     647    Annotation a = null;
     648    if (found != null)
     649    {
     650      a = dc.getItem(Annotation.class, found);
     651    }
     652    else
     653    {
     654      // Create a new annotation
     655      checkPermission(Permission.WRITE);
     656      annotationType.checkPermission(Permission.USE);
     657      // AnnotatableProxy items allow annotations of all types
     658      boolean isAnnotatableProxy = AnnotatableProxy.class.isAssignableFrom(getItemType().getItemClass());
     659      if (!isAnnotatableProxy && !annotationType.isEnabledForItem(getItemType()) && 
     660        !annotationType.isProtocolParameter(getItem().getProtocol()))
     661      {
     662        throw new InvalidAnnotationException(annotationType, getItemType());
     663      }
     664     
     665      a = Annotation.getNewPrimary(dc, this, atd);
     666      dc.saveItemIf(this, a, false);
     667      setSnapshotInvalid();
     668    }
     669   
     670    a.setProjectId(projectId);
     671   
     672    return a;
     673  }
     674 
     675 
     676  /**
    604677    Check if the annotation set contains primary annotation of the specified type.
    605678   
     
    652725 
    653726  /**
     727    Check if the annotation set contains an annotation of the specified type.
     728    If the annotation type has enabled project-specific annotations
     729    (see {@link AnnotationType#getProjectAnnotations()}), this method
     730    will only check if an annotation for the given project exists (or the
     731    default value if projectId=0). Annotations that are specific
     732    for other projects are ignored.
     733   
     734    @param annotationType An <code>AnnotationType</code> object
     735    @param source The source type of the annotation or null if it doesn't matter
     736    @param projectId The project id or 0 to get the default value
     737   
     738    @return TRUE if the annotation set contains an annotation of
     739      the specified type, FALSE otherwise
     740    @since 3.11
     741  */
     742  public boolean hasProjectAnnotation(AnnotationType annotationType, Annotation.Source source, int projectId)
     743  {
     744    if (annotationType == null) return false;
     745   
     746    DbControl dc = getDbControl();
     747    if (!annotationType.getProjectAnnotations()) projectId = 0;
     748   
     749    AnnotationTypeData atd = annotationType.getData();
     750    for (AnnotationData ad : getData().getAnnotations())
     751    {
     752      boolean matchSource = source == null || ad.getSource() == source.ordinal();
     753      // Matches current project or the default value
     754      boolean matchProject = ad.getProjectId() == projectId;
     755      if (matchSource && matchProject && ad.getAnnotationType().equals(atd))
     756      {
     757        return true;
     758      }
     759    }
     760    return false;
     761  }
     762
     763 
     764  /**
    654765    Delete the primary annotation of the specified annotation type.
    655766
     
    669780    throws PermissionDeniedException, InvalidDataException, BaseException
    670781  {
     782    removeProjectAnnotation(annotationType, getSessionControl().getActiveProjectId());
     783  }
     784 
     785  /**
     786    Delete the primary annotation of the specified annotation type.
     787 
     788    Note that this method is intended to be used
     789    with project-specific annotation and will only remove the value
     790    for the given project (or the default value if projectId=0).
     791   
     792    @param annotationType The type of the annotation to delete
     793    @param projectId The project id or 0 to remove the default value
     794    @throws PermissionDeniedException If the logged in user
     795      doesn't have write permission
     796    @throws InvalidDataException If the annotation type is null
     797    @throws BaseException If there is another error
     798    @since 3.11
     799  */
     800  public void removeProjectAnnotation(AnnotationType annotationType, int projectId)
     801    throws PermissionDeniedException, InvalidDataException, BaseException
     802  {
    671803    checkPermission(Permission.WRITE);
    672804    if (annotationType == null) throw new InvalidUseOfNullException("annotationType");
    673805    annotationType.checkPermission(Permission.USE);
     806    if (!annotationType.getProjectAnnotations()) projectId = 0;
    674807   
    675808    DbControl dc = getDbControl();
    676809    int primary = Annotation.Source.PRIMARY.ordinal();
    677     int activeProjectId = annotationType.getProjectAnnotations() ?
    678       dc.getSessionControl().getActiveProjectId() : 0;
    679810    AnnotationTypeData atd = annotationType.getData();
    680811   
     
    684815      AnnotationData a = it.next();
    685816      boolean matchSource = a.getSource() == primary;
    686       boolean matchProject = a.getProjectId() == activeProjectId;
     817      boolean matchProject = a.getProjectId() == projectId;
    687818      if (matchSource && matchProject && a.getAnnotationType().equals(atd))
    688819      {
     
    699830    }
    700831  }
    701  
     832
    702833  /**
    703834    Get a query returning annotations with the specified source.
     835   
     836    If the annotation type is using project-specific annotations
     837    the query will load project-specific values where they exists
     838    and otherwise default values.
    704839
    705840    @param source A source or null to not filter on source
     
    725860    return query;
    726861  }
     862
     863  /**
     864    Get a query returning annotations with the specified source
     865    and belonging to the specified project (or default
     866    values if projectId=0).
     867 
     868    @param source A source or null to not filter on source
     869    @param projectId The project id or 0 to query default values
     870    @since 3.11
     871  */
     872  public ItemQuery<Annotation> getProjectAnnotations(Annotation.Source source, int projectId)
     873  {
     874    ItemQuery<Annotation> query = Annotation.getQuery();
     875    query.restrictPermanent(
     876      Restrictions.eq(
     877        Hql.property("annotationSet"),
     878        Hql.entity(this)
     879    ));
     880    query.restrictPermanent(
     881      Restrictions.eq(
     882        Hql.property("projectId"),
     883        Expressions.integer(projectId)
     884    ));
     885    if (source != null)
     886    {
     887      query.restrictPermanent(
     888        Restrictions.eq(
     889          Hql.property("source"),
     890          Expressions.integer(source.ordinal())
     891      ));
     892    }
     893    return query;
     894  }
     895
    727896 
    728897  /**
  • trunk/src/test/TestAnnotation.java

    r7290 r7296  
    7070    int arrayDesignId = TestArrayDesign.test_create(Platform.GENERIC, true);
    7171    int arrayDesignId2 = TestArrayDesign.test_create(Platform.AFFYMETRIX, true);
     72    int arrayDesignId3 = TestArrayDesign.test_create(Platform.AFFYMETRIX, true);
    7273
    7374    // Create project and a project-specific annotation
     
    116117    int project1AnnotationId = test_annotatate(Item.ARRAYDESIGN, arrayDesignId2, projectSpecificId, 0, "project 1 value");
    117118    test_inherit_annotation(Item.RAWBIOASSAY, rawBioAssayId2, project1AnnotationId, false);
     119   
    118120    TestProject.test_noactive_project();
    119121   
     
    123125      ok = false;
    124126    }
    125 
     127   
    126128    // Check that the annotation values are as expected
    127129    test_project_annotation(Item.ARRAYDESIGN, arrayDesignId2, projectSpecificId, "default value", "project 1 value");
     
    130132    TestProject.test_activate_project(project2, null);
    131133    test_project_annotation(Item.ARRAYDESIGN, arrayDesignId2, projectSpecificId, "default value", "project 1 value");
    132 
     134    TestProject.test_noactive_project();
     135   
     136    // Test new API for working with project-specific values
     137    // Create project-specific value when no project is active
     138    test_annotatate_forceproject(Item.ARRAYDESIGN, arrayDesignId3, projectSpecificId, project1, "project 1 value (3)");
     139
     140    // Create default value when a project is active
     141    TestProject.test_activate_project(project1, null);
     142    test_annotatate_forceproject(Item.ARRAYDESIGN, arrayDesignId3, projectSpecificId, 0, "default value (3)");
     143
     144    // Check the values
     145    test_project_annotation(Item.ARRAYDESIGN, arrayDesignId3, projectSpecificId, "project 1 value (3)", "default value (3)");
     146    TestProject.test_activate_project(project2, null);
     147    test_project_annotation(Item.ARRAYDESIGN, arrayDesignId3, projectSpecificId, "default value (3)", "project 1 value (3)");
     148    TestProject.test_noactive_project();
     149    test_project_annotation(Item.ARRAYDESIGN, arrayDesignId3, projectSpecificId, "default value (3)", "project 1 value (3)");
     150   
     151    // Update project-specific value when no project is active
     152    test_annotatate_forceproject(Item.ARRAYDESIGN, arrayDesignId3, projectSpecificId, project1, "project 1 value (updated)");
     153    // Update default value when a project is active
     154    TestProject.test_activate_project(project1, null);
     155    test_annotatate_forceproject(Item.ARRAYDESIGN, arrayDesignId3, projectSpecificId, 0, "default value (updated)");
     156   
     157    // Check the values
     158    test_project_annotation(Item.ARRAYDESIGN, arrayDesignId3, projectSpecificId, "project 1 value (updated)", "default value (updated)");
     159    TestProject.test_activate_project(project2, null);
     160    test_project_annotation(Item.ARRAYDESIGN, arrayDesignId3, projectSpecificId, "default value (updated)", "project 1 value (updated)");
     161    TestProject.test_noactive_project();
     162    test_project_annotation(Item.ARRAYDESIGN, arrayDesignId3, projectSpecificId, "default value (updated)", "project 1 value (updated)");
    133163   
    134164    if (TestUtil.waitBeforeDelete()) TestUtil.waitForEnter();
     
    162192    TestArrayDesign.test_delete(arrayDesignId);
    163193    TestArrayDesign.test_delete(arrayDesignId2);
     194    TestArrayDesign.test_delete(arrayDesignId3);
    164195    TestAnnotationType.test_delete(projectSpecificId);
    165196    TestAnnotationType.test_delete(intId);
     
    240271  }
    241272
     273  static int test_annotatate_forceproject(Item itemType, int itemId, int annotationTypeId, int projectId, Object... value)
     274  {
     275    if (itemId == 0 || annotationTypeId == 0) return 0;
     276    int id = 0;
     277    DbControl dc = null;
     278    try
     279    {
     280      dc = TestUtil.getDbControl();
     281      Annotatable annotatable = (Annotatable)itemType.getById(dc, itemId);
     282      AnnotationType at = AnnotationType.getById(dc, annotationTypeId);
     283
     284      AnnotationSet as = annotatable.getAnnotationSet();
     285      Annotation a = as.getProjectAnnotation(at, projectId);
     286     
     287      if (value != null)
     288      {
     289        List<Object> l = new ArrayList<Object>();
     290        l.addAll(Arrays.asList(value));
     291        boolean changed = a.setValuesIfDifferent(l, null);
     292      }
     293      dc.commit();
     294      id = a.getId();
     295      dc = TestUtil.getDbControl();
     296      dc.reattachItem(a, false);
     297      write_item(0, a);
     298      write("--Create annotation OK");
     299    }
     300    catch (Throwable ex)
     301    {
     302      write("--Create annotation FAILED");
     303      ex.printStackTrace();
     304      ok = false;
     305    }
     306    finally
     307    {
     308      if (dc != null) dc.close();
     309    }
     310    return id;
     311  }
     312
     313 
    242314  static void test_remove_annotation(Item itemType, int itemId, int annotationTypeId)
    243315  {
Note: See TracChangeset for help on using the changeset viewer.