Changeset 4544


Ignore:
Timestamp:
Sep 25, 2008, 9:01:30 AM (14 years ago)
Author:
Nicklas Nordborg
Message:

References #792: Add support for units to annotation values

  • Made it possible to select a default unit for an annotation type
  • Display annotations with units on the "Annotations" tab in the single-item view
  • Test for displaying annotations with units in list pages on the Array designs list page
Location:
trunk
Files:
5 added
24 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/clients/web/net/sf/basedb/clients/web/DynamicUtil.java

    r4512 r4544  
    212212    Formatter<Boolean> booleanFormatter = FormatterFactory.getBooleanFormatter(sc);
    213213    Formatter<String> stringFormatter = FormatterFactory.getStringFormatter(sc);
    214     Formatter<Object> defaultFormatter = new ToStringFormatter();
     214    Formatter<Object> defaultFormatter = new ToStringFormatter<Object>();
    215215    while (result.hasNext())
    216216    {
  • trunk/src/clients/web/net/sf/basedb/clients/web/formatter/FormatterFactory.java

    r4512 r4544  
    2525
    2626import net.sf.basedb.clients.web.Base;
     27import net.sf.basedb.core.Annotation;
     28import net.sf.basedb.core.AnnotationType;
    2729import net.sf.basedb.core.Coloring;
    2830import net.sf.basedb.core.ExtendedProperty;
    2931import net.sf.basedb.core.SessionControl;
    3032import net.sf.basedb.core.Type;
     33import net.sf.basedb.core.Unit;
    3134import net.sf.basedb.util.ColorGenerator;
    3235import net.sf.basedb.util.formatter.BooleanFormatter;
     
    3538import net.sf.basedb.util.formatter.IntegerFormatter;
    3639import net.sf.basedb.util.formatter.NumberFormatter;
     40import net.sf.basedb.util.formatter.PrefixSuffixFormatter;
    3741
    3842/**
     
    310314  }
    311315
     316  /**
     317    Get a formatter for nice formatting of annotation values, including units.
     318    A base formatter is created by {@link #getTypeFormatter(SessionControl, Type)}
     319    using the value type of the annotation. This formatter is wrapped by
     320    a {@link PrefixSuffixFormatter} if a unit is supplied or the annotation has
     321    a unit.
     322   
     323    @param sc The logged in user's session control
     324    @param a The annotation to get a formatter for
     325    @param unit The unit, or null to use the unit from the annotation
     326      ({@link Annotation#getUnit()}.
     327    @return A formatter object
     328    @since 2.9
     329  */
     330  public static Formatter<?> getAnnotationFormatter(SessionControl sc, Annotation a, Unit unit)
     331  {
     332    AnnotationType at = a.getAnnotationType();
     333    Formatter<?> f = getTypeFormatter(sc, a.getValueType());
     334    return a.getUnitFormatter(f, unit);
     335  }
     336 
    312337}
  • trunk/src/clients/web/net/sf/basedb/clients/web/plugins/SimpleExport.java

    r4523 r4544  
    244244    boolean annotatable = itemType.getItemClass() != null &&
    245245      Annotatable.class.isAssignableFrom(itemType.getItemClass());
    246     MultiFormatter formatter = new MultiFormatter(new ToStringFormatter(), true);
     246    MultiFormatter formatter = new MultiFormatter(new ToStringFormatter<Object>(), true);
    247247    formatter.registerFormatter(Date.class, FormatterFactory.getDateFormatter(sc));
    248248    for (String column : columns)
  • trunk/src/clients/web/net/sf/basedb/clients/web/taglib/table/CellValue.java

    r4512 r4544  
    4040      list=...
    4141      separator=...
     42      prefix=...
     43      suffix=...
    4244   &gt;
    4345</pre>
     
    7173  </tr>
    7274  <tr>
    73     <td>list</td>
     75    <td>separator</td>
    7476    <td>; </td>
    7577    <td>no</td>
    7678    <td>
    7779      A separator to use between each value in a list.
     80    </td>
     81  </tr>
     82  <tr>
     83    <td>prefix</td>
     84    <td></td>
     85    <td>no</td>
     86    <td>
     87      A prefix that is added before each value. Since 2.9.
     88    </td>
     89  </tr>
     90  <tr>
     91    <td>suffix</td>
     92    <td></td>
     93    <td>no</td>
     94    <td>
     95      A suffix that is added after each value. Since 2.9.
    7896    </td>
    7997  </tr>
     
    99117  private Object value = null;
    100118  private Iterable<?> list = null;
     119  private String prefix = "";
     120  private String suffix = "";
    101121  private String separator = "; ";
    102122
     
    132152  {
    133153    return separator;
     154  }
     155
     156  /**
     157    @since 2.9
     158  */
     159  public void setPrefix(String prefix)
     160  {
     161    this.prefix = prefix == null ? "" : prefix;
     162  }
     163  /**
     164    @since 2.9
     165  */
     166  public String getPrefix()
     167  {
     168    return prefix;
     169  }
     170  /**
     171    @since 2.9
     172  */
     173  public void setSuffix(String suffix)
     174  {
     175    this.suffix = suffix == null ? "" : suffix;
     176  }
     177  /**
     178    @since 2.9
     179  */
     180  public String getSuffix()
     181  {
     182    return suffix;
    134183  }
    135184
     
    149198    if (value != null)
    150199    {
    151       output.append(formatter.format(value));
     200      output.append(prefix).append(formatter.format(value)).append(suffix);
    152201      theSeparator = separator;
    153202    }
     
    158207        if (o != null)
    159208        {
    160           output.append(theSeparator).append(formatter.format(o));
     209          output.append(theSeparator).append(prefix).append(formatter.format(o)).append(suffix);
    161210          theSeparator = separator;
    162211        }
  • trunk/src/core/net/sf/basedb/core/Annotation.java

    r4517 r4544  
    2424
    2525import net.sf.basedb.core.data.AnnotationData;
     26import net.sf.basedb.core.data.UnitData;
    2627import net.sf.basedb.core.query.EntityQuery;
    27 
     28import net.sf.basedb.util.formatter.Formatter;
     29import net.sf.basedb.util.formatter.PrefixSuffixFormatter;
     30import net.sf.basedb.util.units.LinearUnitConverter;
     31import net.sf.basedb.util.units.UnitConverter;
     32
     33import java.util.ArrayList;
    2834import java.util.List;
    2935import java.util.Collections;
     
    7480    data.setAnnotationSet(annotationSet.getData());
    7581    data.setAnnotationType(annotationType.getData());
     82    data.setUnit(annotationType.getData().getDefaultUnit());
    7683    data.setValues(annotationType.getValueType().newParameterValueData());
    7784    return a;
     
    199206  }
    200207 
     208  /**
     209    Checks if the values in this annotation has a unit.
     210    @return TRUE if a unit is used, FALSE otherwise
     211    @since 2.9
     212  */
     213  public boolean hasUnit()
     214  {
     215    return getData().getUnit() != null;
     216  }
     217 
     218  /**
     219    Get the unit used for the values in this annotation.
     220    @return A unit, or null if this annotation doesn't use units
     221    @since 2.9
     222  */
     223  public Unit getUnit()
     224  {
     225    return getDbControl().getItem(Unit.class, getData().getUnit());
     226  }
     227 
     228  /**
     229    Get a converter for converting the values in this annotation to
     230    a specific unit.
     231   
     232    @param unit The unit to convert the values to, or null
     233      to convert the values to the unit specified by {@link #getUnit()}
     234    @return A unit converter, or null if this annotation doesn't use units, or
     235      if the specified unit is the same as the default unit of the annotation
     236      type (see {@link AnnotationType#getDefaultUnit()}
     237    @since 2.9
     238  */
     239  public UnitConverter getUnitConverter(Unit unit)
     240  {
     241    UnitConverter converter = null;
     242    UnitData unitData = unit == null ? getData().getUnit() : unit.getData();
     243    if (unitData != null)
     244    {
     245      UnitData reference = getData().getAnnotationType().getDefaultUnit();
     246      if (!unitData.equals(reference))
     247      {
     248        converter = new LinearUnitConverter(
     249            unitData.getReferenceFactor(), unitData.getReferenceOffset(),
     250            reference.getReferenceFactor(), reference.getReferenceOffset());
     251      }
     252    }
     253    return converter;
     254  }
     255
     256  /**
     257    Wrap a parent formatter with a formatter for units. NOTE!
     258    Values should be retrived with the same unit using
     259    {@link #getValues(Unit)} to be correctly formatted with the
     260    returned formatter.
     261   
     262    @param parent The parent formatter, which should be of the
     263      correct type for the annotation values
     264    @param unit The unit to use for formatting, or null to
     265      use the annotation's unit
     266    @return A formatter object
     267    @since 2.9
     268  */
     269  public <T> Formatter<T> getUnitFormatter(Formatter<T> parent, Unit unit)
     270  {
     271    Formatter<T> formatter = parent;
     272    UnitData unitData = unit == null ? getData().getUnit() : unit.getData();
     273    if (unitData == null)
     274    {
     275      unitData = getData().getAnnotationType().getDefaultUnit();
     276    }
     277    if (unitData != null)
     278    {
     279      formatter = new PrefixSuffixFormatter<T>("", parent,
     280          PrefixSuffixFormatter.NBSP + unitData.getDisplaySymbol());
     281    }
     282    return formatter;
     283  }
     284 
     285  /**
     286    Shortcut for <code>Unit.getDisplaySymbol()</code>, but also
     287    checks the default unit of the annotation type.
     288    @param unit The unit to get the symbol for, or null to use
     289      the annotation's unit
     290    @return The unit symbol, or null if this annotation doesn't use
     291      units
     292    @since 2.9
     293  */
     294  public String getUnitSymbol(Unit unit)
     295  {
     296    UnitData unitData = unit == null ? getData().getUnit() : unit.getData();
     297    if (unitData == null)
     298    {
     299      unitData = getData().getAnnotationType().getDefaultUnit();
     300    }
     301    return unitData == null ? null : unitData.getDisplaySymbol();
     302  }
    201303 
    202304  /**
     
    215317    {@link Type} appropriate for the given annotation type, ie. Integer object
    216318    for an integer annotation type. All objects in the list are of the same type.
     319    If this is an annotation with units, the values are returned in the unit
     320    of the annotation type ({@link AnnotationType#getDefaultUnit()}). Use
     321    {@link #getValues(Unit)} to get the values in a different unit, including the
     322    specific unit used on this annotation.
    217323    @see AnnotationType#getValueType()
    218324    @return A list of objects
     
    225331    return Collections.unmodifiableList(values);
    226332  }
     333 
     334  /**
     335    Get the values for this annotation in a specific unit. The values are of a
     336    {@link Type} appropriate for the given annotation type, ie. Integer object
     337    for an integer annotation type. All objects in the list are of the same type.
     338   
     339    @param unit The unit to convert the values to, or null to get the values
     340      in the unit used on this annotation
     341    @see AnnotationType#getValueType()
     342    @return A list of objects
     343    @throws BaseException If there is an error
     344  */
     345  public List<?> getValues(Unit unit)
     346    throws BaseException
     347  {
     348    List<?> values = Values.getItemValues(getDbControl(), getData().getValues().getValues());
     349    UnitConverter converter = getUnitConverter(null);
     350    if (converter != null)
     351    {
     352      List<Object> convertedValues = new ArrayList<Object>(values.size());
     353      Type valueType = getValueType();
     354      for (Object value : values)
     355      {
     356        if (value instanceof Number)
     357        {
     358          value = valueType.convertNumber(converter.convertToSpecificUnit(((Number)value).doubleValue()));
     359        }
     360        convertedValues.add(value);
     361      }
     362      values = convertedValues;
     363    }
     364    return Collections.unmodifiableList(values);
     365  }
     366 
    227367 
    228368  /**
     
    238378  }
    239379
     380  public void setValue(Object value)
     381    throws PermissionDeniedException, InvalidDataException, BaseException
     382  {
     383    setValue(value, null);
     384  }
     385 
    240386  /**
    241387    Set the value of the annotation, replacing any previous values.
    242388    @param value The new value
     389    @param unit The unit of the value, or null to use the default unit of
     390      the annotation type
    243391    @throws PermissionDeniedException If the logged in user doesn't have
    244392      write permission for the annotation or read permission for the annotation
     
    247395      annotation type, see {@link AnnotationType#validateAnnotationValue(Object)}
    248396    @throws BaseException If there is another error
    249   */
    250   public void setValue(Object value)
     397    @since 2.9
     398  */
     399  public void setValue(Object value, Unit unit)
    251400    throws PermissionDeniedException, InvalidDataException, BaseException
    252401  {
    253402    checkPermission(Permission.WRITE);
    254403    AnnotationType annotationType = getAnnotationType();
     404    if (unit == null) unit = annotationType.getDefaultUnit();
     405    UnitConverter converter = getUnitConverter(unit);
     406    if (converter != null && value instanceof Number)
     407    {
     408      value = converter.convertToReferenceUnit(((Number)value).doubleValue());
     409    }
    255410    annotationType.validateAnnotationValue(value);
     411    if (unit != null) getData().setUnit(unit.getData());
    256412    getData().getValues().setSingleValue(Values.getDataValue(value));
    257413  }
    258414
     415  public void setValues(List<?> values)
     416    throws PermissionDeniedException, InvalidDataException, BaseException
     417  {
     418    setValues(values, null);
     419  }
     420 
    259421  /**
    260422    Set the values of the annotation, replacing any previous values.
    261423    @param values A list containing the new values
     424    @param unit The unit of the value, or null to use the default unit of
     425      the annotation type
    262426    @throws PermissionDeniedException If the logged in user doesn't have
    263427      write permission for the annotation or read permission for the annotation
     
    269433    @throws BaseException If there is another error
    270434  */
    271   public void setValues(List<?> values)
     435  public void setValues(List<?> values, Unit unit)
    272436    throws PermissionDeniedException, InvalidDataException, BaseException
    273437  {
    274438    checkPermission(Permission.WRITE);
    275439    AnnotationType annotationType = getAnnotationType();
     440    if (unit == null) unit = annotationType.getDefaultUnit();
    276441    int multiplicity = annotationType.getMultiplicity();
    277442    if (multiplicity > 0 && values.size() > multiplicity)
     
    279444      throw new InvalidDataException("Too many values. Max "+multiplicity+" value(s) are allowed for this annotation type");
    280445    }
     446    UnitConverter converter = getUnitConverter(unit);
     447    List<Object> convertedValues = new ArrayList<Object>(values.size());
     448    Type valueType = getValueType();
    281449    for (Object value : values)
    282450    {
     451      if (converter != null && value instanceof Number)
     452      {
     453        value = valueType.convertNumber(converter.convertToReferenceUnit(((Number)value).doubleValue()));
     454      }
    283455      annotationType.validateAnnotationValue(value);
    284     }
    285     getData().getValues().replaceValues(Values.getDataValues(values));
     456      convertedValues.add(value);
     457    }
     458    if (unit != null) getData().setUnit(unit.getData());
     459    getData().getValues().replaceValues(Values.getDataValues(convertedValues));
    286460  }
    287461
  • trunk/src/core/net/sf/basedb/core/AnnotationType.java

    r4517 r4544  
    2626import net.sf.basedb.core.data.ParameterValueData;
    2727import net.sf.basedb.core.data.AnnotationTypeData;
     28import net.sf.basedb.core.data.QuantityData;
     29import net.sf.basedb.core.data.UnitData;
    2830
    2931import net.sf.basedb.core.query.Restrictions;
     
    300302
    301303  /**
     304    Get the quantity for this annotation type. If the quantity is null,
     305    units are not used with this annotation type. An annotation type without
     306    units can be changed to use units with the {@link #setDefaultUnit(Unit)}
     307    method. Once a unit has been configured, it is not possible to change
     308    the {@link Quantity}.
     309    @return The quantity, or null
     310    @since 2.9
     311  */
     312  public Quantity getQuantity()
     313  {
     314    return getDbControl().getItem(Quantity.class, getData().getQuantity());
     315  }
     316 
     317  /**
     318    Get the default unit for this annotation type.
     319    @return The default unit, or null if this annotation type
     320      doesn't use units
     321    @since 2.9
     322  */
     323  public Unit getDefaultUnit()
     324  {
     325    return getDbControl().getItem(Unit.class, getData().getDefaultUnit());
     326  }
     327 
     328  /**
     329    Set the default unit to use for this annotation type. If the
     330    annotation type doesn't have any current default unit, any
     331    unit can be used. If there already is a default unit, the new
     332    unit must be from the same {@link Quantity} as the existing unit.
     333    <p>
     334    Note! Changing units will result in a convertion of existing
     335    annotation values to the new unit. This may result in loss
     336    of precision due to rounding or truncation errors.
     337    @param defaultUnit The new default unit
     338    @since 2.9
     339  */
     340  public void setDefaultUnit(Unit defaultUnit)
     341  {
     342    checkPermission(Permission.WRITE);
     343    if (isInDatabase())
     344    {
     345      // Make sure that the current quantity is null or the same as for the new unit
     346      QuantityData quantity = getData().getQuantity();
     347      if (quantity != null)
     348      {
     349        if (defaultUnit == null)
     350        {
     351          throw new InvalidUseOfNullException("defaultUnit");
     352        }
     353        else if (!quantity.equals(defaultUnit.getData().getQuantity()))
     354        {
     355          throw new InvalidDataException("The unit '" + defaultUnit.getName() +
     356          "' is not of the expected quantity: " + quantity.getName());
     357        }
     358        if (defaultUnit.getId() != getData().getDefaultUnit().getId())
     359        {
     360          // TODO - recalculate annotation values
     361        }
     362      }
     363    }
     364    getData().setDefaultUnit(defaultUnit == null ? null : defaultUnit.getData());
     365    getData().setQuantity(defaultUnit == null ? null : defaultUnit.getData().getQuantity());
     366  }
     367 
     368  /**
     369    Get a query for returning the units that are considered
     370    "usable" for this annotation type. This is typically used by client
     371    applications to limit the list of choices in the graphical interface.
     372    If this query doesn't return any items, all units should be
     373    consiedered usable.
     374   
     375    @return A query
     376    @since 2.9
     377  */
     378  public ItemQuery<Unit> getUsableUnits()
     379  {
     380    ItemQuery<Unit> query = Unit.getQuery();
     381    query.joinPermanent(Hql.innerJoin("usedUnits", Item.ANNOTATIONTYPE.getAlias()));
     382    query.restrictPermanent(
     383      Restrictions.eq(
     384        Hql.alias(Item.ANNOTATIONTYPE.getAlias()),
     385        Hql.entity(this)
     386      )
     387    );
     388    return query;
     389  }
     390 
     391  /**
     392    Checks if the given unit is a unit that has been specified as
     393    a "usable" unit for this annotation type.
     394    @param unit The unit to check
     395    @return TRUE if the unit is "usable", FALSE otherwise
     396    @since 2.9
     397  */
     398  public boolean isUsableUnit(Unit unit)
     399  {
     400    if (unit == null) return false;
     401    return getData().getUsableUnits().contains(unit.getData());
     402  }
     403 
     404  /**
     405    Add a unit to be used with this annotation type. Note! This doesn't
     406    prevent other units (from the same {@link Quantity}) from being
     407    used. This is just a recommendation to allow client applications
     408    to limit the list of choices in the graphical interface.
     409   
     410    @param unit The unit to add
     411    @throws PermissionDeniedException If the logged in user
     412      doesn't have write permission for the annotation type or
     413      use permission  for the unit
     414    @throws InvalidDataException If the unit is null
     415    @since 2.9
     416  */
     417  public void addUsableUnit(Unit unit)
     418  {
     419    checkPermission(Permission.WRITE);
     420    if (unit == null) throw new InvalidUseOfNullException("unit");
     421    unit.checkPermission(Permission.USE);
     422    getData().getUsableUnits().add(unit.getData());
     423  }
     424 
     425  /**
     426    Remove a unit from this annotation type. This will not affect
     427    already existing annotation with this unit.
     428    @param unit The unit to remove from the set of usable units
     429    @throws PermissionDeniedException If the
     430      logged in user doesn't have {@link Permission#WRITE}
     431      permission for the annotation type
     432    @throws InvalidDataException If the unit is null
     433    @since 2.9
     434  */
     435  public void removeUsableUnit(Unit unit)
     436  {
     437    checkPermission(Permission.WRITE);
     438    if (unit == null) throw new InvalidUseOfNullException("unit");
     439    getData().getUsableUnits().remove(unit.getData());
     440  }
     441
     442  /**
     443    Clear all units.
     444    @since 2.9
     445  */
     446  public void clearUsableUnits()
     447  {
     448    checkPermission(Permission.WRITE);
     449    getData().getUsableUnits().clear();
     450  }
     451
     452  /**
    302453    Get the external id of the annotation type. This value can be used to link
    303454    with information in external databases. It is not used by the BASE core
     
    8521003    return pv == null ? null : Collections.unmodifiableList(Values.getItemValues(getDbControl(), pv.getValues()));
    8531004  }
     1005 
    8541006  /**
    8551007    Set the list of allowed values for an enumerated annotation type.
  • trunk/src/core/net/sf/basedb/core/data/AnnotationTypeData.java

    r4541 r4544  
    4949    Get the quantity this annotation type uses. Can't be changed
    5050    after creation.
    51     @hibernate.many-to-one column="`quantity_id`" not-null="false" outer-join="false"
    52       update="false"
     51    @hibernate.many-to-one column="`quantity_id`" not-null="false" outer-join="false"
    5352    @since 2.9
    5453  */
     
    9796  public Set<UnitData> getUsableUnits()
    9897  {
     98    if (usableUnits == null) usableUnits = new HashSet<UnitData>();
    9999    return usableUnits;
    100100  }
     101 
    101102  /**
    102103    @since 2.9
  • trunk/src/core/net/sf/basedb/core/data/UnitData.java

    r4543 r4544  
    177177  }
    178178
     179  private Set<AnnotationTypeData> usedUnits;
     180  /**
     181    This is the inverse end. It has lazy="false" because we need
     182    automatic deletion from the AnnotationTypeUnits table
     183    @see AnnotationTypeData#getUsableUnits()
     184    @hibernate.set table="`AnnotationTypeUnits`" lazy="true"
     185    @hibernate.collection-key column="`unit_id`"
     186    @hibernate.collection-many-to-many column="`annotationtype_id`"
     187      class="net.sf.basedb.core.data.AnnotationTypeData"
     188  */
     189  Set<AnnotationTypeData> getUsedUnits()
     190  {
     191    return usedUnits;
     192  }
     193  void setUsedUnits(Set<AnnotationTypeData> usedUnits)
     194  {
     195    this.usedUnits = usedUnits;
     196  }
     197 
    179198}
  • trunk/src/core/net/sf/basedb/util/formatter/ToStringFormatter.java

    r4515 r4544  
    3131  @base.modified $Date$
    3232*/
    33 public class ToStringFormatter
    34   implements Formatter<Object>
     33public class ToStringFormatter<T>
     34  implements Formatter<T>
    3535{
    3636
     
    4545    -------------------------------------------
    4646  */
    47   public String format(Object value)
     47  public String format(T value)
    4848  {
    4949    return value == null ? "" : value.toString();
    5050  }
    51   public Number parseString(String value)
     51  public T parseString(String value)
    5252  {
    5353    throw new UnsupportedOperationException("parseString");
  • trunk/src/test/TestAnnotation.java

    r4514 r4544  
    4848    write("++Testing annotations");
    4949    write_header();
     50   
     51    int celsiusId = TestUnit.test_load_by_symbol(SystemItems.getId(Quantity.TEMPERATURE), "C");
     52    int fahrenheitId = TestUnit.test_load_by_symbol(SystemItems.getId(Quantity.TEMPERATURE), "F");
     53    int meterId = TestUnit.test_load_by_symbol(SystemItems.getId(Quantity.LENGTH), "m");
     54    int millimeterId = TestUnit.test_load_by_symbol(SystemItems.getId(Quantity.LENGTH), "mm");
     55
    5056    // Create annotation types and array design
    51 
    52     int intId = TestAnnotationType.test_create(null, Type.INT, 10, null, Item.ARRAYDESIGN, 1, null, true);
    53     int longId = TestAnnotationType.test_create(null, Type.LONG, 1056567756767l, null, Item.ARRAYDESIGN, 1, null, true);
    54     int floatId = TestAnnotationType.test_create(null, Type.FLOAT, 10.1f, null, Item.ARRAYDESIGN, 1, null, true);
    55     int doubleId = TestAnnotationType.test_create(null, Type.DOUBLE, 8.13454646d, null, Item.ARRAYDESIGN, 1, null, true);
    56     int dateId = TestAnnotationType.test_create(null, Type.DATE, new Date(), null, Item.ARRAYDESIGN, 1, null, true);
    57     int textId = TestAnnotationType.test_create(null, Type.TEXT, "default value", null, Item.ARRAYDESIGN, 1, null, true);
    58     int stringId = TestAnnotationType.test_create(null, Type.STRING, "default value", null, Item.ARRAYDESIGN, 1, null, true);
    59     int booleanId = TestAnnotationType.test_create(null, Type.BOOLEAN, true, null, Item.ARRAYDESIGN, 1, null, true);
    60     int enumId = TestAnnotationType.test_create(null, Type.INT, 3, null, Item.ARRAYDESIGN, 1, new Object[] { 1, 2, 3, 4, 5 }, false);
     57    int intId = TestAnnotationType.test_create(null, Type.INT, 0, 10, null, Item.ARRAYDESIGN, 1, null, true);
     58    int longId = TestAnnotationType.test_create(null, Type.LONG, 0, 1056567756767l, null, Item.ARRAYDESIGN, 1, null, true);
     59    int floatId = TestAnnotationType.test_create("Temperature", Type.FLOAT, celsiusId, 10.1f, null, Item.ARRAYDESIGN, 1, null, true);
     60    int doubleId = TestAnnotationType.test_create("Size", Type.DOUBLE, meterId, 8.13454646d, null, Item.ARRAYDESIGN, 1, null, true);
     61    int dateId = TestAnnotationType.test_create(null, Type.DATE, 0, new Date(), null, Item.ARRAYDESIGN, 1, null, true);
     62    int textId = TestAnnotationType.test_create(null, Type.TEXT, 0, "default value", null, Item.ARRAYDESIGN, 1, null, true);
     63    int stringId = TestAnnotationType.test_create(null, Type.STRING, 0, "default value", null, Item.ARRAYDESIGN, 1, null, true);
     64    int booleanId = TestAnnotationType.test_create(null, Type.BOOLEAN, 0, true, null, Item.ARRAYDESIGN, 1, null, true);
     65    int enumId = TestAnnotationType.test_create(null, Type.INT, 0, 3, null, Item.ARRAYDESIGN, 1, new Object[] { 1, 2, 3, 4, 5 }, false);
    6166    int arrayDesignId = TestArrayDesign.test_create(Platform.GENERIC, true);
     67    int arrayDesignId2 = TestArrayDesign.test_create(Platform.AFFYMETRIX, true);
    6268
    6369    // Annotate the array design and list the annotations
    64     int intAnnotationId = test_annotatate(Item.ARRAYDESIGN, arrayDesignId, intId, 10);
    65     int longAnnotationId = test_annotatate(Item.ARRAYDESIGN, arrayDesignId, longId, 1023456789012345l);
    66     int floatAnnotationId = test_annotatate(Item.ARRAYDESIGN, arrayDesignId, floatId, 11.365789456857f);
    67     int doubleAnnotationId = test_annotatate(Item.ARRAYDESIGN, arrayDesignId, doubleId, 11.365789456857d);
    68     int dateAnnotationId = test_annotatate(Item.ARRAYDESIGN, arrayDesignId, dateId, new Date());
    69     int textAnnotationId = test_annotatate(Item.ARRAYDESIGN, arrayDesignId, textId, "a text annotation");
    70     int stringAnnotationId = test_annotatate(Item.ARRAYDESIGN, arrayDesignId, stringId, "a string annotation");
    71     int booleanAnnotationId = test_annotatate(Item.ARRAYDESIGN, arrayDesignId, booleanId, false);
    72     int enumAnnotationId = test_annotatate(Item.ARRAYDESIGN, arrayDesignId, enumId, 4);
     70    int intAnnotationId = test_annotatate(Item.ARRAYDESIGN, arrayDesignId, intId, 0, 10);
     71    int longAnnotationId = test_annotatate(Item.ARRAYDESIGN, arrayDesignId, longId, 0, 1023456789012345l);
     72    int floatAnnotationId = test_annotatate(Item.ARRAYDESIGN, arrayDesignId, floatId, celsiusId, 11.365789456857f);
     73    int doubleAnnotationId = test_annotatate(Item.ARRAYDESIGN, arrayDesignId, doubleId, meterId, 11.365789456857d);
     74    int floatAnnotationId2 = test_annotatate(Item.ARRAYDESIGN, arrayDesignId2, floatId, fahrenheitId, 11.365789456857f);
     75    int doubleAnnotationId2 = test_annotatate(Item.ARRAYDESIGN, arrayDesignId2, doubleId, millimeterId, 11.365789456857d);
     76    int dateAnnotationId = test_annotatate(Item.ARRAYDESIGN, arrayDesignId, dateId, 0, new Date());
     77    int textAnnotationId = test_annotatate(Item.ARRAYDESIGN, arrayDesignId, textId, 0, "a text annotation");
     78    int stringAnnotationId = test_annotatate(Item.ARRAYDESIGN, arrayDesignId, stringId, 0, "a string annotation");
     79    int booleanAnnotationId = test_annotatate(Item.ARRAYDESIGN, arrayDesignId, booleanId, 0, false);
     80    int enumAnnotationId = test_annotatate(Item.ARRAYDESIGN, arrayDesignId, enumId, 0, 4);
    7381    test_list_annotations(Item.ARRAYDESIGN, arrayDesignId, 9);
     82    test_list_annotations(Item.ARRAYDESIGN, arrayDesignId2, 2);
    7483
    7584    // Test: inherit annotations
     
    102111    // Delete annotation types and array design
    103112    TestArrayDesign.test_delete(arrayDesignId);
     113    TestArrayDesign.test_delete(arrayDesignId2);
    104114    TestAnnotationType.test_delete(intId);
    105115    TestAnnotationType.test_delete(longId);
     
    116126  }
    117127 
    118   static int test_annotatate(Item itemType, int itemId, int annotationTypeId, Object... value)
     128  static int test_annotatate(Item itemType, int itemId, int annotationTypeId, int unitId, Object... value)
    119129  {
    120130    if (itemId == 0 || annotationTypeId == 0) return 0;
     
    132142      List<Object> l = new ArrayList<Object>();
    133143      l.addAll(Arrays.asList(value));
    134       a.setValues(l);
     144      Unit unit = unitId != 0 ? Unit.getById(dc, unitId) : null;
     145      a.setValues(l, unit);
    135146      dc.commit();
    136147      id = a.getId();
     
    215226    if (!TestUtil.getSilent())
    216227    {
    217       write("   \tID \tAnnotation type\tAnnotation set\tValues");
    218       write("-- \t-- \t---------------\t--------------\t------");
     228      write("   \tID \tAnnotation type\tAnnotation set\tValues\tUnit\tRaw values");
     229      write("-- \t-- \t---------------\t--------------\t------\t----\t----------");
    219230    }
    220231  }
     
    223234  {
    224235    if (!TestUtil.getSilent())
    225       write(i+":\t"+a.getId()+"\t"+a.getAnnotationType()+"\t"+a.getAnnotationSet()+"\t"+a.getValues());
     236      write(i+":\t"+a.getId()+"\t"+a.getAnnotationType()+"\t"+a.getAnnotationSet()+"\t"+
     237          a.getValues(null) + "\t" + a.getUnit() + "\t" + a.getValues());
    226238  }
    227239  static void write_item(int i, AnnotationSet as)
  • trunk/src/test/TestAnnotationFlatFileImporter.java

    r4514 r4544  
    5656    // Create annotation types, samples and upload file
    5757   
    58     int timeId = TestAnnotationType.test_create("Time (hours)", Type.INT, null, 25, Item.SAMPLE, 1, null, false);
    59     int ageId = TestAnnotationType.test_create("Age (years)", Type.INT, null, 100, Item.SAMPLE, 1, null, false);
    60     int commentId = TestAnnotationType.test_create("Comment", Type.STRING, null, 20, Item.SAMPLE, 0, null, false);
    61     int enumId = TestAnnotationType.test_create("Subtype", Type.STRING, null, null, Item.SAMPLE,
    62       1, new String[] { "alfa", "beta", "gamma" }, false);
     58    int timeId = TestAnnotationType.test_create("Time (hours)", Type.INT, 0, null, 25, Item.SAMPLE, 1, null, false);
     59    int ageId = TestAnnotationType.test_create("Age (years)", Type.INT, 0, null, 100, Item.SAMPLE, 1, null, false);
     60    int commentId = TestAnnotationType.test_create("Comment", Type.STRING, 0, null, 20, Item.SAMPLE, 0, null, false);
     61    int enumId = TestAnnotationType.test_create("Subtype", Type.STRING, 0, null, null,
     62      Item.SAMPLE, 1, new String[] { "alfa", "beta", "gamma" }, false);
    6363
    6464    int sample1 = TestSample.test_create(0, "Sample #1", false);
     
    6969    int sampleDup2 = TestSample.test_create(0, "Sample dup", false);
    7070   
    71     TestAnnotation.test_annotatate(Item.SAMPLE, sample4, timeId, 12);
    72     TestAnnotation.test_annotatate(Item.SAMPLE, sample4, ageId, 2);
    73     TestAnnotation.test_annotatate(Item.SAMPLE, sample4, enumId, "gamma");
    74     TestAnnotation.test_annotatate(Item.SAMPLE, sample4, commentId, "A comment from code");
     71    TestAnnotation.test_annotatate(Item.SAMPLE, sample4, timeId, 0, 12);
     72    TestAnnotation.test_annotatate(Item.SAMPLE, sample4, ageId, 0, 2);
     73    TestAnnotation.test_annotatate(Item.SAMPLE, sample4, enumId, 0, "gamma");
     74    TestAnnotation.test_annotatate(Item.SAMPLE, sample4, commentId, 0, "A comment from code");
    7575   
    7676    int fileId = TestFile.test_create("data/test.annotation.import.txt", false, false);
  • trunk/src/test/TestAnnotationType.java

    r4514 r4544  
    4444    write("++Testing annotation types");
    4545    write_header();
     46   
     47    int celsiusId = TestUnit.test_load_by_symbol(SystemItems.getId(Quantity.TEMPERATURE), "C");
     48    int meterId = TestUnit.test_load_by_symbol(SystemItems.getId(Quantity.LENGTH), "m");
     49   
    4650    // Standard tests: create, load, list
    47     int id_int = test_create(null, Type.INT, 10, null, Item.SAMPLE, 1, null, true);
    48     int id_long = test_create(null, Type.LONG, 123456789123456l, null, Item.SAMPLE, 1, null, true);
    49     int id_float = test_create(null, Type.FLOAT, 10.1, null, Item.SAMPLE, 1, null, false);
    50     int id_double = test_create(null, Type.DOUBLE, 8.13459187745, null, Item.SAMPLE, 1, null, false);
    51     int id_date = test_create(null, Type.DATE, new Date(), null, Item.SAMPLE, 1, null, false);
    52     int id_text = test_create(null, Type.TEXT, "default value", null, Item.SAMPLE, 1, null, false);
    53     int id_string = test_create(null, Type.STRING, "default value", null, Item.SAMPLE, 1, null, false);
    54     int id_boolean = test_create(null, Type.BOOLEAN, true, null, Item.SAMPLE, 1, null, false);
    55     int id_enum = test_create(null, Type.INT, 3, null, Item.SAMPLE, 1, new Object[] { 1, 2, 3, 4, 5 }, false);
    56     int id2_enum = test_create(null, Type.STRING, "one", null, Item.SAMPLE, 1, new String[] { "one", "two ", "three" }, false);
     51    int id_int = test_create(null, Type.INT, 0, 10, null, Item.SAMPLE, 1, null, true);
     52    int id_long = test_create(null, Type.LONG, 0, 123456789123456l, null, Item.SAMPLE, 1, null, true);
     53    int id_float = test_create("Temperature", Type.FLOAT, celsiusId, 10.1, null, Item.SAMPLE, 1, null, false);
     54    int id_double = test_create("Size", Type.DOUBLE, meterId, 8.13459187745, null, Item.SAMPLE, 1, null, false);
     55    int id_date = test_create(null, Type.DATE, 0, new Date(), null, Item.SAMPLE, 1, null, false);
     56    int id_text = test_create(null, Type.TEXT, 0, "default value", null, Item.SAMPLE, 1, null, false);
     57    int id_string = test_create(null, Type.STRING, 0, "default value", null, Item.SAMPLE, 1, null, false);
     58    int id_boolean = test_create(null, Type.BOOLEAN, 0, true, null, Item.SAMPLE, 1, null, false);
     59    int id_enum = test_create(null, Type.INT, 0, 3, null, Item.SAMPLE, 1, new Object[] { 1, 2, 3, 4, 5 }, false);
     60    int id2_enum = test_create(null, Type.STRING, 0, "one", null, Item.SAMPLE, 1, new String[] { "one", "two ", "three" }, false);
    5761    test_load(id_int);
    5862    test_load(id_long);
     
    99103  }
    100104 
    101   static int test_create(String name, Type type, Object defaultValue, Number maxValue, Item itemType, int multiplicity, Object[] enumValues, boolean setAll)
     105  static int test_create(String name, Type type, int unitId,
     106      Object defaultValue, Number maxValue, Item itemType, int multiplicity, Object[] enumValues, boolean setAll)
    102107  {
    103108    if (!TestUtil.hasPermission(Permission.CREATE, Item.ANNOTATIONTYPE)) return 0;
     
    119124      at.setMultiplicity(multiplicity);
    120125      if (name != null) at.setName(name);
     126      if (unitId != 0)
     127      {
     128        at.setDefaultUnit(Unit.getById(dc, unitId));
     129      }
    121130      if (enumValues != null)
    122131      {
     
    241250    if (!TestUtil.getSilent())
    242251    {
    243       write("   \tID \tName      \tDescription\tValue type\tMultiplicity\tExternal ID\tWidth\tHeight\tMiame\tDefault value\tItem types\tEnumeration values");
    244       write("-- \t-- \t--------- \t-----------\t----------\t------------\t-----------\t-----\t------\t-----\t-------------\t----------\t------------------");
     252      write("   \tID \tName      \tDescription\tValue type\tDefault unit\tMultiplicity\tExternal ID\tWidth\tHeight\tMiame\tDefault value\tItem types\tEnumeration values");
     253      write("-- \t-- \t--------- \t-----------\t----------\t------------\t------------\t-----------\t-----\t------\t-----\t-------------\t----------\t------------------");
    245254    }
    246255  }
     
    250259    if (!TestUtil.getSilent())
    251260      write(i+":\t"+at.getId()+"\t"+at.getName()+"\t"+at.getDescription()+"\t"+at.getValueType()+
     261      "\t" + at.getDefaultUnit()+
    252262      "\t"+at.getMultiplicity()+"\t"+at.getExternalId()+"\t"+at.getWidth()+"\t"+at.getHeight()+"\t"+at.isRequiredForMiame()+
    253263      "\t"+at.getDefaultValue()+"\t"+at.getEnabledItems()+"\t"+at.getValues());
  • trunk/src/test/TestAnnotationTypeCategory.java

    r4514 r4544  
    5858
    5959    // extra test: add categories to annotation type
    60     int id_int = TestAnnotationType.test_create(null, Type.INT, 10, null, Item.SAMPLE, 1, null, true);
     60    int id_int = TestAnnotationType.test_create(null, Type.INT, 0, 10, null, Item.SAMPLE, 1, null, true);
    6161    test_add_categories(id_int, id, id2);
    6262    test_list_categories(id_int, 2);
  • trunk/src/test/TestExperiment.java

    r4537 r4544  
    7373   
    7474    // Experimental factors
    75     int factorId1 = TestAnnotationType.test_create("Drug resistance", Type.STRING, null, null, Item.RAWBIOASSAY, 1, new String[] { "high", "medium", "low" }, false);
    76     int factorId2 = TestAnnotationType.test_create("Temperature", Type.FLOAT, null, null, Item.RAWBIOASSAY, 1, null, false);
     75    int factorId1 = TestAnnotationType.test_create("Drug resistance", Type.STRING, 0, null, null, Item.RAWBIOASSAY, 1, new String[] { "high", "medium", "low" }, false);
     76    int factorId2 = TestAnnotationType.test_create("Temperature", Type.FLOAT, 0, null, null, Item.RAWBIOASSAY, 1, null, false);
    7777    test_add_experimental_factors(id, factorId1, factorId2);
    7878    test_list_experimental_factors(id, 2);
  • trunk/src/test/TestWebservices.java

    r4514 r4544  
    127127    TestExperiment.test_add_rawbioassay(experimentId2, rawBioAssayId4);
    128128   
    129     int factorId1 = TestAnnotationType.test_create("Drug resistance", Type.STRING, null, null, Item.RAWBIOASSAY, 1, new String[] { "high", "medium", "low" }, false);
    130     int factorId2 = TestAnnotationType.test_create("Temperature", Type.FLOAT, null, null, Item.RAWBIOASSAY, 1, null, false);
     129    int factorId1 = TestAnnotationType.test_create("Drug resistance", Type.STRING, 0, null, null, Item.RAWBIOASSAY, 1, new String[] { "high", "medium", "low" }, false);
     130    int factorId2 = TestAnnotationType.test_create("Temperature", Type.FLOAT, 0, null, null, Item.RAWBIOASSAY, 1, null, false);
    131131   
    132132    TestExperiment.test_add_experimental_factors(experimentId1, factorId1, factorId2);
    133     TestAnnotation.test_annotatate(Item.RAWBIOASSAY, rawBioAssayId1, factorId1, "medium");
    134     TestAnnotation.test_annotatate(Item.RAWBIOASSAY, rawBioAssayId1, factorId2, 12.5f);
    135     TestAnnotation.test_annotatate(Item.RAWBIOASSAY, rawBioAssayId2, factorId1, "high");
    136     TestAnnotation.test_annotatate(Item.RAWBIOASSAY, rawBioAssayId2, factorId2, 20.3f);
     133    TestAnnotation.test_annotatate(Item.RAWBIOASSAY, rawBioAssayId1, factorId1, 0, "medium");
     134    TestAnnotation.test_annotatate(Item.RAWBIOASSAY, rawBioAssayId1, factorId2, 0, 12.5f);
     135    TestAnnotation.test_annotatate(Item.RAWBIOASSAY, rawBioAssayId2, factorId1, 0, "high");
     136    TestAnnotation.test_annotatate(Item.RAWBIOASSAY, rawBioAssayId2, factorId2, 0, 20.3f);
    137137   
    138138    //Tests
  • trunk/src/webservices/server/net/sf/basedb/ws/server/ReporterService.java

    r4513 r4544  
    108108   
    109109    boolean annotatable = false;
    110     MultiFormatter formatter = new MultiFormatter(new ToStringFormatter(), true);
     110    MultiFormatter formatter = new MultiFormatter(new ToStringFormatter<Object>(), true);
    111111    formatter.registerFormatter(Date.class, FormatterFactory.getDateFormatter(sc));
    112112    List<ExportedProperty> exportedProperties = new ArrayList<ExportedProperty>(extPropInfos.length);   
     
    173173   
    174174    boolean annotatable = false;
    175     MultiFormatter formatter = new MultiFormatter(new ToStringFormatter(), true);
     175    MultiFormatter formatter = new MultiFormatter(new ToStringFormatter<Object>(), true);
    176176    formatter.registerFormatter(Date.class, FormatterFactory.getDateFormatter(sc));
    177177    List<ExportedProperty> exportedProperties = new ArrayList<ExportedProperty>(extPropInfos.length);   
  • trunk/www/WEB-INF/table.tld

    r4510 r4544  
    672672      <rtexprvalue>true</rtexprvalue>
    673673    </attribute>
     674    <attribute>
     675      <name>prefix</name>
     676      <required>false</required>
     677      <rtexprvalue>true</rtexprvalue>
     678    </attribute>
     679    <attribute>
     680      <name>suffix</name>
     681      <required>false</required>
     682      <rtexprvalue>true</rtexprvalue>
     683    </attribute>
    674684  </tag>
    675685
  • trunk/www/admin/annotationtypes/edit_annotationtype.jsp

    r4510 r4544  
    3535  import="net.sf.basedb.core.AnnotationType"
    3636  import="net.sf.basedb.core.AnnotationTypeCategory"
     37  import="net.sf.basedb.core.Quantity"
     38  import="net.sf.basedb.core.Unit"
    3739  import="net.sf.basedb.core.SystemItems"
    3840  import="net.sf.basedb.core.Metadata"
     
    6769  AnnotationType annotationType = null;
    6870  ItemQuery<AnnotationTypeCategory> categoryQuery = null;
     71  ItemQuery<Unit> unitQuery = null;
    6972  Type valueType = null;
     73  boolean readCurrentQuantity = true;
     74  int currentQuantityId = 0;
     75  boolean readCurrentUnit = true;
     76  int currentUnitId = 0;
    7077
    7178  if (itemId == 0)
     
    7380    title = "Create annotation type";
    7481    valueType = Type.valueOf(request.getParameter("value_type"));
     82    unitQuery = Unit.getQuery();
     83   
     84    int recentQuantityId = Values.getInt(cc.getRecent(Item.QUANTITY.name(), 0));
     85    currentQuantityId = Values.getInt(cc.getPropertyValue("quantity"), recentQuantityId);
     86
     87    if (cc.getPropertyFilter("defaultUnit.name") != null)
     88    {
     89      Unit currentUnit = Base.getFirstMatching(dc, Unit.getQuery(), "name", cc.getPropertyFilter("defaultUnit.name"));
     90      if (currentUnit != null) currentUnitId = currentUnit.getId();
     91    }
     92    if (currentUnitId == 0) currentUnitId = Values.getInt(cc.getRecent(Item.UNIT.name(), 0));
    7593    cc.removeObject("item");
    7694  }
     
    85103    categoryQuery.include(Include.ALL);
    86104    categoryQuery.order(Orders.asc(Hql.property("name")));
     105    try
     106    {
     107      Quantity quantity = annotationType.getQuantity();
     108      if (quantity != null)
     109      {
     110        currentQuantityId = quantity.getId();
     111        unitQuery = quantity.getUnits();
     112      }
     113      else
     114      {
     115        unitQuery = Unit.getQuery();
     116      }
     117      Unit unit = annotationType.getDefaultUnit();
     118      if (unit != null) currentUnitId = unit.getId();
     119    }
     120    catch (PermissionDeniedException ex)
     121    {
     122      readCurrentQuantity = false;
     123      readCurrentUnit = false;
     124    }
    87125  }
     126  // Query to retrieve quantities types
     127  final ItemQuery<Quantity> quantityQuery = Quantity.getQuery();
     128  quantityQuery.include(Include.ALL);
     129  quantityQuery.order(Orders.asc(Hql.property("name")));
     130 
     131  unitQuery.include(Include.ALL);
     132  unitQuery.order(Orders.asc(Hql.property("quantity.name")));
     133  unitQuery.order(Orders.asc(Hql.property("referenceFactor")));
     134  unitQuery.order(Orders.asc(Hql.property("name")));
    88135 
    89136  final String clazz = "class=\"text\"";
     
    95142  %>
    96143  <base:page type="popup" title="<%=title%>">
    97   <base:head scripts="tabcontrol.js,linkitems.js" styles="tabcontrol.css">
     144  <base:head scripts="tabcontrol.js,linkitems.js,units.js" styles="tabcontrol.css">
    98145    <script language="JavaScript">
    99146    // Validate the "Annotation type" tab
     
    116163   
    117164    function validateItemTypes()
     165    {
     166      return true;
     167    }
     168
     169    function validateUnits()
    118170    {
    119171      return true;
     
    138190        frm.addCategories.value = Link.getActionIds(1, 'C').join(',');
    139191        frm.removeCategories.value = Link.getActionIds(-1, 'C').join(',');
     192        frm.addUsableUnits.value = Link.getActionIds(1, 'U').join(',');
     193        frm.removeUsableUnits.value = Link.getActionIds(-1, 'U').join(',');
    140194        frm.submit();
    141195      }
     
    156210      initItemTypes();
    157211      initCategories();
     212      initUnits();
    158213      interfaceOnClick();
    159214    }
     215
     216    function initUnits()
     217    {
     218      var frm = document.forms['annotationType'];
     219      var quantityList = frm.quantity_id;
     220      var unitList = frm.unit_id;
     221      var quantity;
     222      <%
     223      if (unitQuery != null)
     224      {
     225        List<Unit> units = unitQuery.list(dc);
     226        Quantity prevQuantity = null;
     227        for (Unit unit : units)
     228        {
     229          Quantity quantity = unit.getQuantity();
     230          if (!quantity.equals(prevQuantity))
     231          {
     232            %>
     233            quantity = new Quantity(<%=quantity.getId()%>, '<%=HTML.javaScriptEncode(quantity.getName())%>');
     234            <%
     235            prevQuantity = quantity;
     236          }
     237          %>
     238          quantity.addUnit(<%=unit.getId()%>, '<%=HTML.javaScriptEncode(unit.getName())%>', '<%=HTML.javaScriptEncode(unit.getDisplaySymbol())%>');
     239          <%
     240        }
     241      }
     242      %>
     243      Quantities.populateLists(quantityList, unitList, <%=currentQuantityId%>, <%=currentUnitId%>);
     244      Quantities.updateUnitsList(quantityList[quantityList.selectedIndex].quantity, frm.allUnits);
     245      var usableUnits = new Array();
     246      <%
     247      if (annotationType != null)
     248      {
     249        ItemQuery<Unit> usableQuery = annotationType.getUsableUnits();
     250        usableQuery.include(Include.ALL);
     251        usableQuery.order(Orders.asc(Hql.property("referenceFactor")));
     252        usableQuery.order(Orders.asc(Hql.property("name")));
     253       
     254        ItemResultList<Unit> usableUnits = usableQuery.list(dc);
     255        for (Unit u : usableUnits)
     256        {
     257          %>
     258          usableUnits['ID<%=u.getId()%>'] = true;
     259          <%
     260        }
     261      }
     262      %>
     263      addUsableUnits(usableUnits);
     264    }
     265   
     266    function quantityOnChange()
     267    {
     268      var frm = document.forms['annotationType'];
     269      var quantityList = frm.quantity_id;
     270      var unitList = frm.unit_id;
     271      var quantity = quantityList[quantityList.selectedIndex].quantity;
     272      Quantities.updateUnitsList(quantity, unitList, <%=currentUnitId%>);
     273      Quantities.updateUnitsList(quantity, frm.allUnits);
     274    }
     275
     276    function addUsableUnits(usableUnits)
     277    {
     278      var frm = document.forms['annotationType'];
     279      for (var i = 0; i < frm.allUnits.length; i++)
     280      {
     281        var option = frm.allUnits[i];
     282        if (option.selected || (usableUnits && usableUnits['ID'+option.value]))
     283        {
     284          addUsableUnit(option.value, option.text, usableUnits);
     285          frm.allUnits[i--] = null;
     286        }
     287      }
     288    }
     289
     290    function addUsableUnit(unitId, name, asNew)
     291    {
     292      var item = Link.getItem('U', unitId);
     293      if (!item) item = new Item('U', unitId, name);
     294      if (asNew)
     295      {
     296        Link.addNewItem(document.forms['annotationType'].usableUnits, item);
     297      }
     298      else
     299      {
     300        Link.addItem(document.forms['annotationType'].usableUnits, item);
     301      }
     302    }
     303    function removeUsableUnits()
     304    {
     305      var frm = document.forms['annotationType'];
     306      Link.removeSelected(frm.usableUnits, frm.allUnits);
     307    }
     308   
    160309    function initItemTypes()
    161310    {
     
    682831    </t:tab>
    683832   
     833    <t:tab id="units" title="Units" validate="validateUnits()"
     834      visible="<%=valueType.isNumerical()%>" helpid="annotationtype.edit.units">
     835   
     836      <table class="form" cellspacing="0">
     837      <tr valign="top">
     838        <td class="prompt">Quantity</td>
     839        <td>
     840          <select name="quantity_id" onchange="quantityOnChange()"
     841            <%=!readCurrentQuantity || (annotationType != null && currentQuantityId != 0) ?
     842                "disabled readonly class=\"disabled\"" : "class=\"unchangeable\""%>>
     843            <option value="">- do not use units -
     844          </select>
     845        </td>
     846      </tr>
     847      <tr valign="top">
     848        <td class="prompt">Default unit</td>
     849        <td>
     850          <select name="unit_id"
     851            <%=!readCurrentQuantity ? "disabled readonly class=\"disabled\"" : ""%>>
     852          </select>
     853        </td>
     854      </tr>
     855      </table>
     856
     857      <base:note type="warning" style="background: #ffffd8;"  visible="<%=annotationType != null %>">
     858      Changing the default unit triggers a conversion of existing annotation values
     859      to the new unit. This may result in loss of precision due to rounding or
     860      truncation.
     861      </base:note>
     862
     863      <table class="form" cellspacing="0">
     864      <tr valign="top">
     865        <td class="prompt">Use units</td>
     866        <td></td>
     867        <td class="prompt">Do not use</td>
     868      </tr>
     869      <tr>
     870        <td>
     871          <select name="usableUnits" size="5" multiple style="width: 15em;">
     872          </select>
     873        </td>
     874        <td>
     875           <br>
     876          <base:button
     877            onclick="addUsableUnits()"
     878            title="<img src='../../images/move_left.gif' alt='' style='vertical-align: middle;'>"
     879            tooltip="Enable the annotation type for the selected unit(s)"
     880          /><p>
     881          <base:button
     882            onclick="removeUsableUnits()"
     883            title="<img src='../../images/move_right.gif' alt='' style='vertical-align: middle;'>"
     884            tooltip="Disable the annotation type for the selected unit(s)"
     885          />
     886          <br>
     887        </td>
     888        <td>
     889          <select name="allUnits" size="5" multiple style="width: 15em;">
     890          </select>
     891          <input type="hidden" name="removeUsableUnits" value="">
     892          <input type="hidden" name="addUsableUnits" value="">
     893        </td>
     894      </tr>
     895      </table>
     896
     897      <div align=right>
     898        <i><base:icon image="unchangeable.gif" /> = can't be changed later</i>
     899      </div>
     900
     901    </t:tab>
     902
    684903    <t:tab id="categories" title="Categories" validate="validateCategories()"
    685904      helpid="annotationtype.edit.categories">
  • trunk/www/admin/annotationtypes/index.jsp

    r4510 r4544  
    3131  import="net.sf.basedb.core.AnnotationType"
    3232  import="net.sf.basedb.core.AnnotationTypeCategory"
     33  import="net.sf.basedb.core.Quantity"
     34  import="net.sf.basedb.core.Unit"
    3335  import="net.sf.basedb.core.ItemQuery"
    3436  import="net.sf.basedb.core.Permission"
     
    132134    // Update the properties on an item (will close the popup)
    133135    ItemContext cc = Base.getAndSetCurrentContext(sc, itemType, null, defaultContext);
     136    final int maxRecent = Base.getMaxRecent(sc);
    134137    dc = sc.newDbControl();
    135138    AnnotationType annotationType = (AnnotationType)cc.getObject("item");
     
    245248    }
    246249   
     250    // Units tab
     251    if (valueType.isNumerical())
     252    {
     253      int unitId = Values.getInt(request.getParameter("unit_id"), -1);
     254      if (unitId >= 0) // < 0 = denied or unchanged
     255      {
     256        Unit unit = unitId == 0 ? null : Unit.getById(dc, unitId);
     257        annotationType.setDefaultUnit(unit);
     258        if (unit != null) cc.setRecent(unit, maxRecent);
     259      }
     260     
     261      String[] removeUnits = Values.getString(request.getParameter("removeUsableUnits")).split(",");
     262      for (int i = 0; i < removeUnits.length; ++i)
     263      {
     264        int uId = Values.getInt(removeUnits[i], -1);
     265        if (uId != -1) annotationType.removeUsableUnit(Unit.getById(dc, uId));
     266      }
     267     
     268      String[] addUnits = Values.getString(request.getParameter("addUsableUnits")).split(",");
     269      for (int i = 0; i < addUnits.length; ++i)
     270      {
     271        int uId = Values.getInt(addUnits[i], -1);
     272        if (uId != -1) annotationType.addUsableUnit(Unit.getById(dc, uId));
     273      }
     274    }
     275   
    247276    // Categories tab
    248277    String[] removeCategories = Values.getString(request.getParameter("removeCategories")).split(",");
  • trunk/www/admin/annotationtypes/list_annotationtypes.jsp

    r4510 r4544  
    3030  import="net.sf.basedb.core.AnnotationType"
    3131  import="net.sf.basedb.core.AnnotationTypeCategory"
     32  import="net.sf.basedb.core.Quantity"
    3233  import="net.sf.basedb.core.ItemQuery"
    3334  import="net.sf.basedb.core.Include"
     
    100101  categoryQuery.order(Orders.asc(Hql.property("name")));
    101102
     103  // Get all quantities
     104  final ItemQuery<Quantity> quantityQuery = Quantity.getQuery();
     105  quantityQuery.order(Orders.asc(Hql.property("name")));
     106  quantityQuery.setCacheResult(true);
     107
    102108  Map<Plugin.MainType, Integer> pluginCount = PluginDefinition.countPlugins(dc, guiContext);
    103109  try
     
    285291        enumeration="<%=types%>"
    286292        title="Value type"
     293        sortable="true"
     294        filterable="true"
     295        exportable="true"
     296      />
     297      <%
     298      Enumeration<String, String> quantities = new Enumeration<String, String>();
     299      quantities.add("", "- none -");
     300      for (Quantity q : quantityQuery.list(dc))
     301      {
     302        quantities.add(Integer.toString(q.getId()), q.getName());
     303      }
     304      %>
     305      <tbl:columndef
     306        id="quantity"
     307        property="quantity"
     308        sortproperty="quantity.name"
     309        exportproperty="quantity.name"
     310        datatype="int"
     311        enumeration="<%=quantities%>"
     312        title="Quantity"
     313        sortable="true"
     314        filterable="true"
     315        exportable="true"
     316      />
     317      <tbl:columndef
     318        id="defaultUnit"
     319        property="defaultUnit.name"
     320        datatype="string"
     321        title="Default unit"
    287322        sortable="true"
    288323        filterable="true"
     
    541576                <tbl:cell column="externalId"><%=HTML.encodeTags(item.getExternalId())%></tbl:cell>
    542577                <tbl:cell column="valueType"><%=item.getValueType()%></tbl:cell>
     578                <tbl:cell column="quantity"><base:propertyvalue
     579                    item="<%=item%>"
     580                    property="quantity"
     581                    enableEditLink="<%=mode.hasEditLink()%>"
     582                    enablePropertyLink="<%=mode.hasPropertyLink()%>"
     583                  /></tbl:cell>
     584                <tbl:cell column="defaultUnit"><base:propertyvalue
     585                    item="<%=item%>"
     586                    property="defaultUnit"
     587                    enableEditLink="<%=mode.hasEditLink()%>"
     588                    enablePropertyLink="<%=mode.hasPropertyLink()%>"
     589                  /></tbl:cell>
    543590                <tbl:cell column="isEnumeration"><%=item.isEnumeration() ?
    544591                  HTML.encodeTags(Values.getString(item.getValues(), ", ", true)) :
  • trunk/www/admin/annotationtypes/view_annotationtype.jsp

    r4510 r4544  
    247247        <td class="prompt">Value type</td>
    248248        <td><%=annotationType.getValueType()%></td>
     249      </tr>
     250      <tr>
     251        <td class="prompt">Default unit</td>
     252        <td><base:propertyvalue item="<%=annotationType%>" property="quantity" />
     253          <base:icon image="submenu.gif" style="vertical-align: baseline;"/>
     254          <base:propertyvalue item="<%=annotationType%>" property="defaultUnit" />
     255        </td>
    249256      </tr>
    250257      <tr>
  • trunk/www/common/annotations/list_annotations.jsp

    r4510 r4544  
    3434  import="net.sf.basedb.core.Annotatable"
    3535  import="net.sf.basedb.core.AnnotatableProxy"
     36  import="net.sf.basedb.core.Unit"
    3637  import="net.sf.basedb.core.Nameable"
    3738  import="net.sf.basedb.core.ItemQuery"
     
    4344  import="net.sf.basedb.clients.web.util.HTML"
    4445  import="net.sf.basedb.util.formatter.Formatter"
     46  import="net.sf.basedb.util.formatter.PrefixSuffixFormatter"
    4547  import="net.sf.basedb.clients.web.formatter.FormatterFactory"
    4648  import="net.sf.basedb.util.Values"
     
    102104    inheritedAnnotations = inheritedQuery.list(dc);
    103105  }
    104   %>
     106%>
    105107  <base:page type="include">
    106108  <base:body>
     
    163165            for (AnnotationType at : annotationTypes)
    164166            {
    165               Formatter formatter = FormatterFactory.getTypeFormatter(sc, at.getValueType());
    166               List<?> values = as == null || !as.hasAnnotation(at) ?
    167                 null : as.getAnnotation(at).getValues();
     167              Annotation a = as != null && as.hasAnnotation(at) ? as.getAnnotation(at) : null;
     168              List<?> values = null;
     169              Formatter formatter = null;
     170              if (a != null)
     171              {
     172                values = a.getValues(null);
     173                formatter = FormatterFactory.getAnnotationFormatter(sc, a, null);
     174              }
    168175              if (values != null || !at.isRemoved())
    169176              {
    170                 %>
     177              %>
    171178                <tbl:row>
    172179                  <tbl:cell column="annotation"><base:icon
     
    198205        <%
    199206      }
     207      char nbsp = 0xA0;
    200208      %>
    201 
     209      :<%="a" + nbsp + "b" %>:
    202210      <%
    203211      if (protocolParameters != null )
     
    241249              for (AnnotationType at : protocolParameters)
    242250              {
    243                 Formatter formatter = FormatterFactory.getTypeFormatter(sc, at.getValueType());
    244                 List<?> values = as == null || !as.hasAnnotation(at) ?
    245                   null : as.getAnnotation(at).getValues();
     251                Annotation a = as != null && as.hasAnnotation(at) ? as.getAnnotation(at) : null;
     252                Formatter formatter = null;
     253                List<?> values = null;
     254                if (a != null)
     255                {
     256                  values = a.getValues(null);
     257                  formatter = FormatterFactory.getAnnotationFormatter(sc, a, null);
     258                }
    246259                if (values != null || !at.isRemoved())
    247260                {
     
    329342              boolean writeInherited = false;
    330343              String icon = "joust/annotation.gif";
    331               Formatter formatter = FormatterFactory.getTypeFormatter(sc, a.getValueType());
     344              Formatter formatter = FormatterFactory.getAnnotationFormatter(sc, a, null);
    332345              try
    333346              {
  • trunk/www/include/scripts/linkitems.js

    r4510 r4544  
    132132    the links are also registered.
    133133    @param list The list object
    134   */
    135   this.removeSelected = function(list)
     134    @param addTo An optional list item to add the removed items to
     135  */
     136  this.removeSelected = function(list, addTo)
    136137  {
    137138    for (var i = 0; i < list.length; i++)
     
    143144        listItem.value = 0;
    144145        list[i--] = null;
     146        if (addTo)
     147        {
     148          addTo[addTo.length] = new Option(listItem.name, listItem.id, false, true);
     149        }
    145150      }
    146151    }
  • trunk/www/lims/arraydesigns/list_designs.jsp

    r4511 r4544  
    3636  import="net.sf.basedb.core.AnnotationType"
    3737  import="net.sf.basedb.core.AnnotationSet"
     38  import="net.sf.basedb.core.Annotation"
    3839  import="net.sf.basedb.core.Job"
    3940  import="net.sf.basedb.core.ItemQuery"
     
    699700                    if (as.hasAnnotation(at))
    700701                    {
     702                      Annotation a = as.getAnnotation(at);
    701703                      %>
    702                       <tbl:cell column="<%="at"+at.getId()%>"
     704                      <tbl:cell
     705                        column="<%="at"+at.getId()%>"
    703706                        ><tbl:cellvalue
    704                         list="<%=as.getAnnotation(at).getValues()%>"
     707                          list="<%=a.getValues(null)%>"
     708                          suffix="<%="&nbsp;"+a.getUnitSymbol(null)%>"
    705709                      /></tbl:cell>
    706710                      <%
Note: See TracChangeset for help on using the changeset viewer.