Changeset 4131
- Timestamp:
- Feb 8, 2008, 9:55:48 AM (15 years ago)
- Location:
- trunk
- Files:
-
- 1 added
- 32 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/doc/src/docbook/appendix/incompatible.xml
r4127 r4131 105 105 since this is a new option. In most cases, it should be handled in the same way 106 106 as if the job is <constant>EXECUTING</constant>. 107 </para> 108 109 <bridgehead>Hybridization to Labeled extracts link</bridgehead> 110 <para> 111 This link can now hold information about which sub-array a labeled 112 extract belongs to on a multi-array hybridization. Code that is 113 unaware of the concept of sub-arrays may find hybridizations 114 where the number of labeled extracts doesn't match the number 115 channels in the platform used, and that more than one labeled 116 extract has the same label. This was previously considered as 117 an error condition by the experiment validator. With the 118 new scheme the validation has to be done on a sub-array basis instead 119 of on the complete hybridization. 120 </para> 121 122 <para> 123 A similar issue arises when inheriting annotations to a raw bioassay 124 which stems from a specific sub-array on a mutli-array hybridization. 125 This raw bioassay should only inherit annotations from the labeled 126 extracts that are part of the same sub-array. For API compatibility 127 reasons the <methodname>Hybridization.getAnnotatableParents()</methodname> 128 will still return <emphasis>all</emphasis> labeled extracts. Code 129 that is calling this method in order to find the parents to 130 a raw bioassay should instead call the new method, 131 <methodname>Hybridizations.getAnnotatableParents(int)</methodname>, 132 where the <code>int</code> is the sub-array index value for 133 the raw bioassay. 107 134 </para> 108 135 -
trunk/doc/src/docbook/userdoc/array_lims.xml
r3950 r4131 54 54 For users buying their arrays from commercial sources, the plate management component 55 55 can be ignored and they can go immediately to 56 <xref linkend="array_lims.arraydesign" /> 57 . 56 <xref linkend="array_lims.arraydesign" />. 58 57 </para> 59 58 … … 120 119 files that can be selected on the <guilabel>Data files</guilabel> 121 120 tab. 121 </para> 122 </listitem> 123 </varlistentry> 124 <varlistentry> 125 <term> 126 <guilabel>Arrays/slide</guilabel> 127 </term> 128 <listitem> 129 <para> 130 The number of sub-arrays that can be placed on a 131 single slide. The default value is 1, but some 132 platforms, for example Illumina, has slides 133 with 6 or 8 arrays. 122 134 </para> 123 135 </listitem> -
trunk/doc/src/docbook/userdoc/biomaterials.xml
r3950 r4131 914 914 <sect3 id="biomaterials.hybridizations.hybridization"> 915 915 <title>Hybridization</title> 916 <variablelist> 917 <varlistentry> 918 <term> 919 <guilabel>Name</guilabel> 920 </term> 921 <listitem> 922 <para> 923 <replaceable>New hybridization</replaceable> 924 is BASE2 default name but it is strongly advise to provide a 925 meaningful and unique name (required). 926 </para> 927 </listitem> 928 </varlistentry> 929 930 <varlistentry> 931 <term> 932 <guilabel>Created</guilabel> 933 </term> 934 <listitem> 935 <para> 936 A date should be provided. The information can be important when 937 running quality controls on data and account for potential 938 confounding factor (e.g. to account for a day effect) 939 </para> 940 </listitem> 941 </varlistentry> 942 943 <varlistentry> 944 <term> 945 <guilabel>Registered</guilabel> 946 </term> 947 <listitem> 948 <para> 949 This field is automatically populated with a date at which the 950 hybridization was entered in BASE2 system. 951 </para> 952 </listitem> 953 </varlistentry> 954 955 <varlistentry> 956 <term> 957 <guilabel>Protocol</guilabel> 958 </term> 959 <listitem> 960 <para> 961 The hybridization protocol that was used to do the hybridization. 962 </para> 963 </listitem> 964 </varlistentry> 965 966 <varlistentry> 967 <term> 968 <guilabel>Hardware</guilabel> 969 </term> 970 <listitem> 971 <para> 972 The hybridization-station that was used during the hybridization. 973 </para> 974 </listitem> 975 </varlistentry> 976 977 <varlistentry> 978 <term> 979 <guilabel>Array slide</guilabel> 980 </term> 981 <listitem> 982 <para>The array slide that was used in the hybridization.</para> 983 <note> 984 <para> 985 Ideally, The Array Slides should have been created but for those 986 users with permission to do, Array Slides could be generated at 987 that point. 988 </para> 989 </note> 990 </listitem> 991 </varlistentry> 992 993 <varlistentry> 994 <term> 995 <guilabel>Description</guilabel> 996 </term> 997 <listitem> 998 <para> 999 A free text field to report any information that can not be captured 1000 otherwise 1001 </para> 1002 </listitem> 1003 </varlistentry> 1004 </variablelist> 916 1005 917 <figure id="write_docbook_doc.figures.hybridization-tab-1"> 1006 918 <title>Hybridization tab</title> … … 1013 925 </mediaobject> 1014 926 </screenshot> 1015 </figure> 1016 </sect3> 927 </figure> 928 929 <helptext external_id="hybridization.edit" title="Edit hybridization"> 930 <variablelist> 931 <varlistentry> 932 <term> 933 <guilabel>Name</guilabel> 934 </term> 935 <listitem> 936 <para> 937 <replaceable>New hybridization</replaceable> 938 is BASE2 default name but it is strongly advise to provide a 939 meaningful and unique name (required). 940 </para> 941 </listitem> 942 </varlistentry> 943 <varlistentry> 944 <term> 945 <guilabel>Arrays</guilabel> 946 </term> 947 <listitem> 948 <para> 949 The number of sub-arrays on the slide that was used in 950 this hybridization. The default value is 1, but some 951 platforms, for example Illumina, has slides 952 with 6 or 8 arrays. 953 </para> 954 </listitem> 955 </varlistentry> 956 957 <varlistentry> 958 <term> 959 <guilabel>Created</guilabel> 960 </term> 961 <listitem> 962 <para> 963 A date should be provided. The information can be important when 964 running quality controls on data and account for potential 965 confounding factor (e.g. to account for a day effect) 966 </para> 967 </listitem> 968 </varlistentry> 969 970 <varlistentry> 971 <term> 972 <guilabel>Registered</guilabel> 973 </term> 974 <listitem> 975 <para> 976 This field is automatically populated with a date at which the 977 hybridization was entered in BASE2 system. 978 </para> 979 </listitem> 980 </varlistentry> 981 982 <varlistentry> 983 <term> 984 <guilabel>Protocol</guilabel> 985 </term> 986 <listitem> 987 <para> 988 The hybridization protocol that was used to do the hybridization. 989 </para> 990 </listitem> 991 </varlistentry> 992 993 <varlistentry> 994 <term> 995 <guilabel>Hardware</guilabel> 996 </term> 997 <listitem> 998 <para> 999 The hybridization-station that was used during the hybridization. 1000 </para> 1001 </listitem> 1002 </varlistentry> 1003 1004 <varlistentry> 1005 <term> 1006 <guilabel>Array slide</guilabel> 1007 </term> 1008 <listitem> 1009 <para>The array slide that was used in the hybridization.</para> 1010 <note> 1011 <para> 1012 Ideally, The Array Slides should have been created but for those 1013 users with permission to do, Array Slides could be generated at 1014 that point. 1015 </para> 1016 </note> 1017 </listitem> 1018 </varlistentry> 1019 1020 <varlistentry> 1021 <term> 1022 <guilabel>Description</guilabel> 1023 </term> 1024 <listitem> 1025 <para> 1026 A free text field to report any information that can not be captured 1027 otherwise 1028 </para> 1029 </listitem> 1030 </varlistentry> 1031 </variablelist> 1032 <seeother> 1033 <other external_id="hybridization.labeledextracts">Labeled extracts</other> 1034 </seeother> 1035 </helptext> 1036 1037 </sect3> 1038 1017 1039 <sect3 id="biomaterials.hybridizations.properties.labeledextracts"> 1018 1040 <title>Labeled extracts</title> 1041 1042 <helptext external_id="hybridization.labeledextracts" title="Labeled extracts"> 1019 1043 <para> 1020 1044 This important tab allows users to select the labeled extracts applied to an … … 1022 1046 </para> 1023 1047 <para> 1024 Use the buttons to the right for adding and removing the items in the list. 1025 Select one or several labeled extracts in the list and write the used mass in 1026 the field below. 1027 </para> 1048 Use the <guibutton>Add labeled extracts</guibutton> button to add 1049 items and the <guibutton>Remove</guibutton> button to remove items. 1050 Select one or several labeled extracts in the list and write the used 1051 mass and sub-array index in the fields below. 1052 </para> 1053 1054 <seeother> 1055 <other external_id="hybridization.edit">Edit hybridization</other> 1056 </seeother> 1057 </helptext> 1058 1028 1059 </sect3> 1029 1060 <sect3 id="biomaterials.hybridizations.properties.annotations_param"> -
trunk/doc/src/docbook/userdoc/rawbioassays.xml
r4093 r4131 96 96 <para> 97 97 The name of the raw bioassay. 98 </para> 99 </listitem> 100 </varlistentry> 101 <varlistentry> 102 <term> 103 <guilabel>Array index</guilabel> 104 </term> 105 <listitem> 106 <para> 107 The index of the sub-array on the hybridization this 108 raw bioassay's data is linked with. The default value 109 is 1. With some platforms, for example Illumina, which has 110 slides with 6 or 8 arrays the value should be changed to 111 reflect the correct sub-array. This information is important 112 to link the raw bioassay with the correct biomaterial 113 entries. 98 114 </para> 99 115 </listitem> -
trunk/src/core/common-queries.xml
r4127 r4131 3052 3052 </query> 3053 3053 3054 <query id="SET_NUMARRAYS_ON_ARRAYDESIGNS" type="HQL"> 3055 <sql> 3056 UPDATE ArrayDesignData ad 3057 SET ad.numArrays = :numArrays 3058 WHERE ad.numArrays IS NULL 3059 </sql> 3060 <description> 3061 A HQL query that set the numArrays property 3062 on all array designs with a null value. 3063 </description> 3064 </query> 3065 3066 <query id="SET_NUMARRAYS_ON_HYBRIDIZATIONS" type="HQL"> 3067 <sql> 3068 UPDATE HybridizationData hyb 3069 SET hyb.numArrays = :numArrays 3070 WHERE hyb.numArrays IS NULL 3071 </sql> 3072 <description> 3073 A HQL query that set the numArrays property 3074 on all hybridizations with a null value. 3075 </description> 3076 </query> 3077 3078 <query id="SET_ARRAYNUM_ON_RAWBIOASSAYS" type="HQL"> 3079 <sql> 3080 UPDATE RawBioAssayData rba 3081 SET rba.arrayNum = :arrayNum 3082 WHERE rba.arrayNum IS NULL 3083 </sql> 3084 <description> 3085 A HQL query that set the arrayNum property 3086 on all raw bioassays with a null value. 3087 </description> 3088 </query> 3089 3054 3090 </predefined-queries> -
trunk/src/core/net/sf/basedb/core/ArrayDesign.java
r4080 r4131 107 107 ad.setName("New array design"); 108 108 ad.setPlatform(platform); 109 ad.setNumArrays(1); 109 110 ad.getData().setFeatureIdentificationMethod(FeatureIdentificationMethod.NONE.getValue()); 110 111 return ad; … … 127 128 ad.setName("New array design"); 128 129 ad.setVariant(variant); 130 ad.setNumArrays(1); 129 131 ad.getData().setFeatureIdentificationMethod(FeatureIdentificationMethod.NONE.getValue()); 130 132 return ad; … … 414 416 415 417 /** 418 Get the number of arrays on a single slide. The default 419 value is 1. 420 @since 2.6 421 */ 422 public int getNumArrays() 423 { 424 return getData().getNumArrays(); 425 } 426 427 /** 428 Set the number of arrays on a single slide. 429 @param numArrays A value > 0 430 @throws NumberOutOfRangeException If the value is <=0 431 @since 2.6 432 */ 433 public void setNumArrays(int numArrays) 434 { 435 checkPermission(Permission.WRITE); 436 if (numArrays <= 0) throw new NumberOutOfRangeException("numArrays", numArrays, 0, false); 437 getData().setNumArrays(numArrays); 438 } 439 440 /** 416 441 Check if this array design has information about features, either 417 442 in the database or in files. -
trunk/src/core/net/sf/basedb/core/BioMaterialEvent.java
r4026 r4131 453 453 } 454 454 } 455 455 456 /** 457 Get the source group of the biomaterial 458 @param bioMaterial The source biomaterial 459 @return The source group index or 0 if the biomaterial is not a source of this event 460 @since 2.6 461 @see #setSourceGroup(MeasuredBioMaterial, int) 462 */ 463 public int getSourceGroup(MeasuredBioMaterial bioMaterial) 464 { 465 UsedQuantity used = getData().getSources().get(bioMaterial.getData()); 466 return used != null ? used.getSourceGroup() : 0; 467 } 468 456 469 /** 457 470 Add a source biomaterial to this event or update … … 476 489 checkAllowedSource(bioMaterial); 477 490 MeasuredBioMaterialData data = (MeasuredBioMaterialData)bioMaterial.getData(); 478 UsedQuantity used = getData().getSources().put(data, new UsedQuantity(usedQuantity)); 479 if (used != null && used.getUsedQuantity() != null) 480 { 481 MeasuredBioMaterial.updateRemainingQuantity(getDbControl(), data, -used.getUsedQuantity()); 482 } 483 if (usedQuantity != null) MeasuredBioMaterial.updateRemainingQuantity(getDbControl(), data, usedQuantity); 484 } 485 486 491 UsedQuantity used = getData().getSources().get(data); 492 if (used != null) 493 { 494 // There is already an entry for this biomaterial 495 if (used.getUsedQuantity() != null) 496 { 497 // Return the old value to the biomaterial 498 MeasuredBioMaterial.updateRemainingQuantity(getDbControl(), data, -used.getUsedQuantity()); 499 } 500 // Set the new value 501 used.setUsedQuantity(usedQuantity); 502 } 503 else 504 { 505 // This is a new entry 506 used = new UsedQuantity(usedQuantity, 1); 507 getData().getSources().put(data, used); 508 } 509 if (usedQuantity != null) 510 { 511 // Take away the new used quantity from the biomaterial 512 MeasuredBioMaterial.updateRemainingQuantity(getDbControl(), data, usedQuantity); 513 } 514 } 515 516 /** 517 Set the source group for a biomaterial that was used as a source in this event. 518 The source group value is used to group sources that are related. The only 519 example and use case is when using multi-array hybridizations 520 where it is essential to know which array a labeled extract goes on. The source 521 group is then simply the number of the array on the hybridization and should be 522 a value between 1 and {@link Hybridization#getNumArrays()}. 523 <p> 524 A single labeled extract can only be used in a single group. Experiments using a n-channel 525 platform and a common reference must first split the reference labeled extract 526 into individual labeled extracts. This can be done with the 'pooling' functionality. 527 <p> 528 If the source biomaterial has not been added with {@link #addSource(MeasuredBioMaterial, Float)} 529 this method will automatically add it with usedQuantity = null. 530 531 @param bioMaterial The source biomaterial 532 @param sourceGroup The source group 533 @throws PermissionDeniedException If the logged in user doesn't have 534 write permission for this event of use permission for the source 535 @throws InvalidDataException If this is not a creation event or 536 if the produced biomaterial is not pooled or if the source 537 biomaterial is of another type 538 @since 2.6 539 */ 540 public void setSourceGroup(MeasuredBioMaterial bioMaterial, int sourceGroup) 541 throws PermissionDeniedException, InvalidDataException, BaseException 542 { 543 checkPermission(Permission.WRITE); 544 if (bioMaterial == null) throw new InvalidUseOfNullException("bioMaterial"); 545 MeasuredBioMaterialData data = (MeasuredBioMaterialData)bioMaterial.getData(); 546 547 UsedQuantity used = getData().getSources().get(data); 548 if (used == null) 549 { 550 addSource(bioMaterial, null); 551 used = getData().getSources().get(data); 552 } 553 if (used != null) 554 { 555 used.setSourceGroup(sourceGroup); 556 } 557 } 558 487 559 /** 488 560 Remove a biomaterial used as a source. Sources can only be specified if … … 572 644 { 573 645 MeasuredBioMaterialData data = (MeasuredBioMaterialData)bioMaterial.getData(); 574 getData().getSources().put(data, new UsedQuantity(usedQuantity ));646 getData().getSources().put(data, new UsedQuantity(usedQuantity, 1)); 575 647 if (usedQuantity != null) MeasuredBioMaterial.updateRemainingQuantity(getDbControl(), data, usedQuantity); 576 648 } … … 597 669 /** 598 670 Get a query that return all biomaterials used as sources 599 for this event. 671 for this event. To filter or sort on the used quantity or source group columns 672 use 'srcevt' as alias. For example to only return sources in source group 2 673 and to order the result by used quantity: 674 <pre class="code"> 675 query = event.getSources(); 676 query.restrict(Expressions.gteq( 677 Hql.property("srcevt", "sourceGroup"), Expressions.integer(2)) 678 ); 679 query.order(Orders.asc(Hql.property("srcevt", "usedQuantity"))); 680 </pre> 681 <p> 682 NOTE! The filtering and ordering on the "srcevt" columns is only 683 available in BASE 2.6 and later. 684 600 685 @return An {@link ItemQuery} object 601 686 */ … … 648 733 } 649 734 650 query.joinPermanent(Hql.innerJoin("sourceEvents", TYPE.getAlias())); 735 query.joinPermanent(Hql.innerJoin("sourceEvents", "srcevt")); 736 query.joinPermanent(Hql.innerJoin("srcevt", "event", TYPE.getAlias())); 651 737 query.restrictPermanent( 652 738 Restrictions.eq( -
trunk/src/core/net/sf/basedb/core/Hybridization.java
r4020 r4131 26 26 27 27 import net.sf.basedb.core.data.HybridizationData; 28 import net.sf.basedb.core.query.Expressions; 28 29 import net.sf.basedb.core.query.Restrictions; 29 30 import net.sf.basedb.core.query.Hql; … … 63 64 Hybridization h = dc.newItem(Hybridization.class); 64 65 h.setName("New hybridization"); 66 h.setNumArrays(1); 65 67 return h; 66 68 } … … 117 119 */ 118 120 /** 119 Get the labeled extracts and array slide. 121 Get the labeled extracts and array slide. Note that this method will 122 include all labeled extracts, even on multi-array hybridizations. 123 @see #getAnnotatableParents(int) 120 124 */ 121 125 public Set<Annotatable> getAnnotatableParents() 122 126 throws BaseException 123 127 { 124 Set<Annotatable> annotatable = new HashSet<Annotatable>(); 125 try 126 { 127 ItemQuery<? extends MeasuredBioMaterial> sources = getCreationEvent().getSources(); 128 sources.include(Include.MINE, Include.SHARED, Include.OTHERS, Include.IN_PROJECT); 129 annotatable.addAll(sources.list(getDbControl())); 130 if (getData().getArraySlide() != null) annotatable.add(getArraySlide()); 131 } 132 catch (PermissionDeniedException ex) 133 {} 134 return annotatable; 128 return getAnnotatableParents(0); 135 129 } 136 130 /** … … 202 196 // ------------------------------------------- 203 197 198 /** 199 Get the labeled extracts, possible on a specific array, 200 and the array slide. 201 @param arrayNum If > 0 only include the labeled extracts which has been 202 linked with {@link BioMaterialEvent#setSourceGroup(MeasuredBioMaterial, int)}. 203 If <= 0 include all labeled extracts. 204 */ 205 @SuppressWarnings("unchecked") 206 public Set<Annotatable> getAnnotatableParents(int arrayNum) 207 throws BaseException 208 { 209 Set<Annotatable> annotatable = new HashSet<Annotatable>(); 210 try 211 { 212 BioMaterialEvent event = getCreationEvent(); 213 ItemQuery<LabeledExtract> query = (ItemQuery<LabeledExtract>)event.getSources(); 214 query.include(Include.MINE, Include.SHARED, Include.OTHERS, Include.IN_PROJECT); 215 if (arrayNum > 0) 216 { 217 query.restrict( 218 Restrictions.eq( 219 Hql.property("srcevt", "sourceGroup"), 220 Expressions.integer(arrayNum) 221 ) 222 ); 223 } 224 annotatable.addAll(query.list(getDbControl())); 225 if (getData().getArraySlide() != null) annotatable.add(getArraySlide()); 226 } 227 catch (PermissionDeniedException ex) 228 {} 229 return annotatable; 230 } 231 204 232 /** 205 233 Get the event that represents the creation of this hybridization. … … 259 287 260 288 /** 289 Get the number of arrays that on the slide. 290 The default value is 1. 291 @since 2.6 292 */ 293 public int getNumArrays() 294 { 295 return getData().getNumArrays(); 296 } 297 /** 298 Set the number of arrays on a single slide. The value should be 299 between 1 and the number of arrays specified by the connected array 300 design. The latter requiremed is not checked. 301 @param numArrays A value > 0 302 @throws NumberOutOfRangeException If the value is <=0 303 @since 2.6 304 */ 305 public void setNumArrays(int numArrays) 306 { 307 checkPermission(Permission.WRITE); 308 if (numArrays <= 0) throw new NumberOutOfRangeException("numArrays", numArrays, 0, false); 309 getData().setNumArrays(numArrays); 310 } 311 312 /** 261 313 Create a new {@link Scan} from this hybridization. 262 314 -
trunk/src/core/net/sf/basedb/core/Install.java
r4113 r4131 107 107 method. 108 108 */ 109 public static final int NEW_SCHEMA_VERSION = Integer.valueOf(5 1).intValue();109 public static final int NEW_SCHEMA_VERSION = Integer.valueOf(52).intValue(); 110 110 111 111 public static synchronized void createTables(boolean update, final ProgressReporter progress) -
trunk/src/core/net/sf/basedb/core/RawBioAssay.java
r4128 r4131 146 146 rba.setPlatform(platform, rawDataType); 147 147 rba.setName("New raw bioassay"); 148 rba.setArrayNum(1); 148 149 return rba; 149 150 } … … 170 171 rba.setVariant(variant, rawDataType); 171 172 rba.setName("New raw bioassay"); 173 rba.setArrayNum(1); 172 174 return rba; 173 175 } … … 601 603 getData().setVariant(variant); 602 604 getData().setRawDataType(rawDataType.getId()); 605 } 606 607 /** 608 Get the hybridization array number this raw bioassay is linked to. 609 The default value is 1. 610 @since 2.6 611 */ 612 public int getArrayNum() 613 { 614 return getData().getArrayNum(); 615 } 616 /** 617 Set the hybridization array number this raw bioassay is linked to. The value should be 618 between 1 and the number of arrays specified by the hybridization, 619 {@link Hybridization#getNumArrays()}. 620 @param arrayNum A value > 0 621 @throws NumberOutOfRangeException If the value is <=0 622 @since 2.6 623 */ 624 public void setArrayNum(int arrayNum) 625 { 626 checkPermission(Permission.WRITE); 627 if (arrayNum <= 0) throw new NumberOutOfRangeException("arrayNum", arrayNum, 0, false); 628 getData().setArrayNum(arrayNum); 603 629 } 604 630 -
trunk/src/core/net/sf/basedb/core/Update.java
r4113 r4131 593 593 </tr> 594 594 595 <tr> 596 <td>52</td> 597 <td> 598 <ul> 599 <li>Added {@link net.sf.basedb.core.data.ArrayDesignData#getNumArrays()}. The update 600 sets the value to 1 for all existing array designs. 601 <li>Added {@link net.sf.basedb.core.data.HybridizationData#getNumArrays()}. The update 602 sets the value to 1 for all existing hybridizations. 603 </ul> 604 </td> 605 </tr> 606 595 607 </table> 596 608 … … 842 854 if (progress != null) progress.display((int)(50*progress_factor), "--Updating schema version: " + schemaVersion + " -> 51..."); 843 855 schemaVersion = updateToSchemaVersion51(session); 856 } 857 858 if (schemaVersion < 52) 859 { 860 if (progress != null) progress.display((int)(51*progress_factor), "--Updating schema version: " + schemaVersion + " -> 52..."); 861 schemaVersion = setSchemaVersionInTransaction(session, 52); 844 862 } 845 863 … … 2515 2533 } 2516 2534 2535 if (schemaVersion < 52) 2536 { 2537 // Set numArrays = 1 on all array designs 2538 org.hibernate.Query query = HibernateUtil.getPredefinedQuery(session, 2539 "SET_NUMARRAYS_ON_ARRAYDESIGNS"); 2540 /* 2541 UPDATE ArrayDesignData ad 2542 SET ad.numArrays = :numArrays 2543 WHERE ad.numArrays IS NULL 2544 */ 2545 query.setInteger("numArrays", 1); 2546 HibernateUtil.executeUpdate(query); 2547 2548 // Set numArrays = 1 on all hybridizations 2549 query = HibernateUtil.getPredefinedQuery(session, 2550 "SET_NUMARRAYS_ON_HYBRIDIZATIONS"); 2551 /* 2552 UPDATE HybridizationData hyb 2553 SET hyb.numArrays = :numArrays 2554 WHERE hyb.numArrays IS NULL 2555 */ 2556 query.setInteger("numArrays", 1); 2557 HibernateUtil.executeUpdate(query); 2558 2559 // Set arrayNum = 1 on all raw bioassays 2560 query = HibernateUtil.getPredefinedQuery(session, 2561 "SET_ARRAYNUM_ON_RAWBIOASSAYS"); 2562 /* 2563 UPDATE RawBioAssayData rba 2564 SET rba.arrayNum = :arrayNum 2565 WHERE rba.arrayNum IS NULL 2566 */ 2567 query.setInteger("arrayNum", 1); 2568 HibernateUtil.executeUpdate(query); 2569 } 2570 2517 2571 // Commit the changes 2518 2572 HibernateUtil.commit(tx); -
trunk/src/core/net/sf/basedb/core/data/ArrayDesignData.java
r4083 r4131 92 92 } 93 93 94 private int numArrays; 95 /** 96 The number of arrays on a single slide. 97 @hibernate.property column="`num_arrays`" type="int" not-null="true" 98 @since 2.6 99 */ 100 public int getNumArrays() 101 { 102 return numArrays; 103 } 104 public void setNumArrays(int numArrays) 105 { 106 this.numArrays = numArrays; 107 } 108 94 109 private boolean affyChip; 95 110 /** -
trunk/src/core/net/sf/basedb/core/data/HybridizationData.java
r3948 r4131 75 75 this.creationEvent = creationEvent; 76 76 } 77 78 private int numArrays; 79 /** 80 The number of arrays that was hybridized. Shold be <= ArrayDesign.numArrays 81 @hibernate.property column="`num_arrays`" type="int" not-null="true" 82 @since 2.6 83 */ 84 public int getNumArrays() 85 { 86 return numArrays; 87 } 88 public void setNumArrays(int numArrays) 89 { 90 this.numArrays = numArrays; 91 } 77 92 78 93 private Set<ScanData> scans; -
trunk/src/core/net/sf/basedb/core/data/MeasuredBioMaterialData.java
r3948 r4131 135 135 } 136 136 137 private Set<BioMaterial EventData> sourceEvents;137 private Set<BioMaterialSourceEventData> sourceEvents; 138 138 /** 139 139 This is the inverse end. … … 141 141 @hibernate.set table="`BioMaterialEventSources`" lazy="true" inverse="true" 142 142 @hibernate.collection-key column="`biomaterial_id`" 143 @hibernate.collection-many-to-many column="`event_id`" class="net.sf.basedb.core.data.BioMaterialEventData" 143 @hibernate.collection-composite-element class="net.sf.basedb.core.data.BioMaterialSourceEventData" 144 hibernate.collection-many-to-many column="`event_id`" class="net.sf.basedb.core.data.BioMaterialEventData" 144 145 */ 145 Set<BioMaterial EventData> getSourceEvents()146 Set<BioMaterialSourceEventData> getSourceEvents() 146 147 { 147 148 return sourceEvents; 148 149 } 149 void setSourceEvents(Set<BioMaterial EventData> sourceEvents)150 void setSourceEvents(Set<BioMaterialSourceEventData> sourceEvents) 150 151 { 151 152 this.sourceEvents = sourceEvents; -
trunk/src/core/net/sf/basedb/core/data/RawBioAssayData.java
r3948 r4131 111 111 } 112 112 113 private int arrayNum; 114 /** 115 The array number this raw bioassay is linked to on a multi-array 116 hybridization. 117 @hibernate.property column="`array_num`" type="int" not-null="true" 118 @since 2.6 119 */ 120 public int getArrayNum() 121 { 122 return arrayNum; 123 } 124 public void setArrayNum(int arrayNum) 125 { 126 this.arrayNum = arrayNum; 127 } 128 113 129 private ScanData scan; 114 130 /** -
trunk/src/core/net/sf/basedb/core/data/UsedQuantity.java
r4084 r4131 31 31 where the element is null, so we have to use a composite element instead. This class 32 32 maps the <code>used_quantity</code> column in the <code>BioMaterialEventSources<code> table. 33 <p> 34 NOTE! In 2.6 we changed the 'dummy' property to {@link #getSourceGroup()} 35 to be able to solve the problem with multi-array hybridizations. The name 36 of this class is now a bit misleading but we see no real reason to rename 37 it since it is part of the internal API only. 38 <p> 39 NOTE! The inverse relation is mapped in {@link MeasuredBioMaterialData#getSourceEvents()} 40 and uses the {@link BioMaterialSourceEventData} class to hold the same columns 41 as this class. 33 42 34 43 @author Nicklas … … 60 69 @param usedQuantity The used quantity 61 70 */ 62 public UsedQuantity(Float usedQuantity )71 public UsedQuantity(Float usedQuantity, int sourceGroup) 63 72 { 64 73 this.usedQuantity = usedQuantity; 74 this.sourceGroup = sourceGroup; 65 75 } 66 76 … … 73 83 return usedQuantity; 74 84 } 75 void setUsedQuantity(Float usedQuantity)85 public void setUsedQuantity(Float usedQuantity) 76 86 { 77 87 this.usedQuantity = usedQuantity; 78 88 } 79 89 90 private int sourceGroup; 80 91 /** 81 Dummy non-null value to force Hibernate to save the link. 92 This column was previously used as a 'dummy' value for making 93 sure that links were not deleted when the used quantity was 94 null. From 2.6 this column is used to group sources together. 95 This is needed to support multi-array hybridizations where we 96 need to know the array each labeled extract goes into. 82 97 @hibernate.property column="`dummy`" type="int" not-null="true" 98 @since 2.6 83 99 */ 84 int getDummy()100 public int getSourceGroup() 85 101 { 86 return 1;102 return sourceGroup; 87 103 } 88 void setDummy(int dummy) 89 {} 90 91 /** 92 Check if this object is equal to another <code>UsedQuantity</code> 93 object. They are considered to be the same if the contains the same 94 non-null value. 95 */ 96 public boolean equals(Object o) 104 public void setSourceGroup(int sourceGroup) 97 105 { 98 if ((o == null) || (getClass() != o.getClass())) return false; 99 UsedQuantity uq = (UsedQuantity)o; 100 return this.usedQuantity == null ? false : this.usedQuantity.equals(uq.usedQuantity); 106 this.sourceGroup = sourceGroup; 101 107 } 102 108 103 /**104 Calculate the hash code for the object.105 */106 public int hashCode()107 {108 return usedQuantity == null ? 0 : usedQuantity.hashCode();109 }110 109 /** 111 110 Convert the value to a string. -
trunk/www/common/annotations/inherit.jsp
r3679 r4131 40 40 import="net.sf.basedb.core.AnnotationSet" 41 41 import="net.sf.basedb.core.Annotation" 42 import="net.sf.basedb.core.RawBioAssay" 43 import="net.sf.basedb.core.Hybridization" 42 44 import="net.sf.basedb.core.ItemQuery" 43 45 import="net.sf.basedb.core.Include" … … 63 65 <%@ taglib prefix="base" uri="/WEB-INF/base.tld" %> 64 66 <%! 65 private void loadParents(Set<AnnotationSet> parentAnnotations, Set<Annotatable> parentItems, Annotatable item)67 private void loadParents(Set<AnnotationSet> parentAnnotations, Set<Annotatable> parentItems, Annotatable current, Annotatable root) 66 68 { 67 Set<Annotatable> parents = item.getAnnotatableParents(); 69 Set<Annotatable> parents = null; 70 if (root instanceof RawBioAssay && current instanceof Hybridization) 71 { 72 Hybridization hyb = (Hybridization)current; 73 RawBioAssay rba = (RawBioAssay)root; 74 parents = hyb.getAnnotatableParents(rba.getArrayNum()); 75 } 76 else 77 { 78 parents = current.getAnnotatableParents(); 79 } 68 80 if (parents == null) return; 69 81 for (Annotatable parent : parents) … … 75 87 parentAnnotations.add(parent.getAnnotationSet()); 76 88 } 77 loadParents(parentAnnotations, parentItems, parent );89 loadParents(parentAnnotations, parentItems, parent, root); 78 90 } 79 91 } … … 174 186 { 175 187 // Get all annotated parents and their annotation sets 176 loadParents(parentAnnotations, parentItems, item );188 loadParents(parentAnnotations, parentItems, item, item); 177 189 } 178 190 if (standalone) … … 206 218 parentAnnotations.add(parentItem.getAnnotationSet()); 207 219 } 208 loadParents(parentAnnotations, parentItems, parentItem );220 loadParents(parentAnnotations, parentItems, parentItem, item); 209 221 } 210 222 } … … 245 257 } 246 258 %> 247 259 <base:page type="popup" title="Inherit annotations"> 248 260 <base:head scripts="annotations.js,parameters.js,newjoust.js,linkitems.js" styles="parameters.css,newjoust.css"> 249 261 <script language="JavaScript"> -
trunk/www/lims/arraydesigns/edit_design.jsp
r4029 r4131 156 156 return false; 157 157 } 158 else if (!Numbers.isInteger(frm.numArrays.value)) 159 { 160 alert("'" + frm.numArrays.value + "' is not a valid number"); 161 frm.numArrays.focus(); 162 return false; 163 } 164 else if (parseInt(frm.numArrays.value) <= 0) 165 { 166 alert("Number of arrays must be > 0"); 167 frm.numArrays.focus(); 168 return false; 169 } 158 170 return true; 159 171 } … … 301 313 </td> 302 314 </tr> 315 <tr> 316 <td class="prompt">Arrays / slide</td> 317 <td><input <%=requiredClazz%> type="text" name="numArrays" 318 value="<%=design == null ? Values.getString(cc.getPropertyValue("numArrays"), "1") : design.getNumArrays()%>" 319 size="12" maxlength="10" onkeypress="return Numbers.integerOnly(event)"></td> 320 </tr> 303 321 <tr valign=top> 304 322 <td class="prompt">Description</td> -
trunk/www/lims/arraydesigns/index.jsp
r3909 r4131 176 176 design.setName(Values.getStringOrNull(request.getParameter("name"))); 177 177 design.setDescription(Values.getStringOrNull(request.getParameter("description"))); 178 design.setNumArrays(Values.getInt(request.getParameter("numArrays"), 1)); 178 179 179 180 // Data files tab -
trunk/www/lims/arraydesigns/list_designs.jsp
r4093 r4131 327 327 enumeration="<%=fiMethods%>" 328 328 /> 329 <tbl:columndef 330 id="numArrays" 331 property="numArrays" 332 datatype="int" 333 title="Arrays/slide" 334 sortable="true" 335 filterable="true" 336 exportable="true" 337 /> 329 338 <tbl:columndef 330 339 id="batches" … … 569 578 <tbl:cell column="numFileFeatures"><%=item.getNumFileFeatures()%></tbl:cell> 570 579 <tbl:cell column="featureIdentificationMethod"><%=item.getFeatureIdentificationMethod()%></tbl:cell> 580 <tbl:cell column="numArrays"><%=item.getNumArrays()%></tbl:cell> 571 581 <tbl:cell column="batches"> 572 582 <% -
trunk/www/lims/arraydesigns/view_design.jsp
r4093 r4131 299 299 <td class="prompt">Variant</td> 300 300 <td><base:propertyvalue item="<%=design%>" property="variant" /></td> 301 </tr> 302 <tr> 303 <td class="prompt">Arrays per slide</td> 304 <td><%=design.getNumArrays()%></td> 301 305 </tr> 302 306 <tr> -
trunk/www/views/hybridizations/edit_hybridization.jsp
r3888 r4131 197 197 return false; 198 198 } 199 else if (!Numbers.isInteger(frm.numArrays.value)) 200 { 201 alert("'" + frm.numArrays.value + "' is not a valid number"); 202 frm.numArrays.focus(); 203 return false; 204 } 205 else if (parseInt(frm.numArrays.value) <= 0) 206 { 207 alert("Number of arrays must be > 0"); 208 frm.numArrays.focus(); 209 return false; 210 } 199 211 return true; 200 212 } … … 369 381 { 370 382 var item = Link.getItem('L', labeledExtractId); 371 if (!item) item = new Item('L', labeledExtractId, name+' [-]', '', '');383 if (!item) item = new Item('L', labeledExtractId, '1: '+name+' [-]', '', ''); 372 384 Link.addItem(document.forms['hybridization'].labeled_extracts, item); 373 385 parentsChanged = true; 386 labeledExtractsOnChange(); 374 387 } 375 388 function labeledExtractsOnChange() … … 379 392 if (item && item.id) 380 393 { 381 frm.used_quantity.value = item.value; 394 var i = item.value.indexOf(':'); 395 frm.used_quantity.value = i > 0 ? item.value.substring(0, i) : item.value; 396 frm.array_index.value = i > 0 ? item.value.substring(i+1) : '1'; 382 397 frm.used_quantity.focus(); 383 398 } … … 385 400 { 386 401 frm.used_quantity.value = ''; 402 frm.array_index.value = '1'; 387 403 } 388 404 } … … 391 407 var frm = document.forms['hybridization']; 392 408 var usedQuantity = frm.used_quantity.value; 409 var arrayIndex = frm.array_index.value; 410 var numArrays = parseInt(frm.numArrays.value); 411 if (arrayIndex != '' && (arrayIndex > numArrays || arrayIndex <= 0)) 412 { 413 alert('Array index must be between 1 and ' + numArrays); 414 return; 415 } 416 if (arrayIndex == '') arrayIndex = 1; 393 417 var displayQuantity = usedQuantity == '' ? '-' : usedQuantity+' µg'; 394 418 for (var i = 0; i < frm.labeled_extracts.length; i++) // > … … 397 421 if (option.selected && option.item.id) 398 422 { 399 option.item.value = usedQuantity ;423 option.item.value = usedQuantity + ':' + arrayIndex; 400 424 var text = option.text.replace(/\[.*\]/, '['+displayQuantity+']'); 425 text = text.replace(/\d*\:/, arrayIndex + ':'); 401 426 option.text = text; 402 427 } 403 428 } 429 } 430 function arrayIndexOnBlur() 431 { 432 usedQuantityOnBlur(); 404 433 } 405 434 … … 429 458 LabeledExtract e = LabeledExtract.getById(dc, id); 430 459 %> 431 Link.addItem(labeledExtracts, new Item('L', <%=e.getId()%>, ' <%=HTML.javaScriptEncode(e.getName())%> [-]', '', ''));460 Link.addItem(labeledExtracts, new Item('L', <%=e.getId()%>, '1: <%=HTML.javaScriptEncode(e.getName())%> [-]', '', '')); 432 461 <% 433 462 } … … 440 469 { 441 470 String usedQuantity = Values.formatNumber(creationEvent.getUsedQuantity(le), -1); 471 int arrayIndex = creationEvent.getSourceGroup(le); 442 472 %> 443 Link.addNewItem(labeledExtracts, new Item('L', <%=le.getId()%>, '<%= HTML.javaScriptEncode(le.getName())%> [<%=usedQuantity%> µg]', '<%=usedQuantity%>'));473 Link.addNewItem(labeledExtracts, new Item('L', <%=le.getId()%>, '<%=arrayIndex + ": " + HTML.javaScriptEncode(le.getName())%> [<%=usedQuantity%> µg]', '<%=usedQuantity%>:<%=arrayIndex%>')); 444 474 <% 445 475 } … … 464 494 value="<%=name%>" 465 495 size="40" maxlength="<%=Hybridization.MAX_NAME_LENGTH%>"></td> 496 </tr> 497 <tr> 498 <td class="prompt">Arrays</td> 499 <td><input <%=requiredClazz%> type="text" name="numArrays" 500 value="<%=hyb == null ? Values.getString(cc.getPropertyValue("numArrays"), "1") : hyb.getNumArrays()%>" 501 size="12" maxlength="10" onkeypress="return Numbers.integerOnly(event)"></td> 466 502 </tr> 467 503 <tr> … … 553 589 554 590 <t:tab id="labeledExtracts" title="Labeled extracts" helpid="hybridization.labeledextracts"> 591 <input type="hidden" name="modifiedLabeledExtracts" value=""> 592 <input type="hidden" name="removedLabeledExtracts" value=""> 555 593 556 594 <table class="form" cellspacing=0> … … 564 602 onchange="labeledExtractsOnChange()"> 565 603 </select> <br> 566 Used567 <input <%=clazz%> type="text" name="used_quantity" value=""568 size="12" maxlength="10" onkeypress="return Numbers.numberOnly(event)"569 onkeyup="usedQuantityOnBlur()"570 > (µg)571 <input type="hidden" name="modifiedLabeledExtracts" value="">572 <input type="hidden" name="removedLabeledExtracts" value="">573 574 604 </td> 575 605 <td> … … 591 621 </td> 592 622 </tr> 623 624 <tr> 625 <td style="text-align: right; padding-right: 5px;">- used quantity</td> 626 <td> 627 <input <%=clazz%> type="text" name="used_quantity" value="" 628 size="12" maxlength="10" onkeypress="return Numbers.numberOnly(event)" 629 onkeyup="usedQuantityOnBlur()" 630 > (µg) 631 </td> 632 </tr> 633 634 <tr> 635 <td style="text-align: right; padding-right: 5px;">- array index</td> 636 <td> 637 <input <%=clazz%> type="text" name="array_index" value="" 638 size="12" maxlength="10" onkeypress="return Numbers.numberOnly(event)" 639 onkeyup="arrayIndexOnBlur()" 640 > 641 </td> 642 </tr> 643 593 644 </table> 594 645 </t:tab> -
trunk/www/views/hybridizations/index.jsp
r3679 r4131 155 155 hyb.setName(Values.getStringOrNull(request.getParameter("name"))); 156 156 hyb.setDescription(Values.getStringOrNull(request.getParameter("description"))); 157 hyb.setNumArrays(Values.getInt(request.getParameter("numArrays"), 1)); 158 157 159 int arraySlideId = Values.getInt(request.getParameter("arrayslide_id"), -1); 158 160 if (arraySlideId >= 0) // < 0 = denied or unchanged … … 188 190 { 189 191 LabeledExtract le = LabeledExtract.getById(dc, leId); 190 creationEvent.addSource(le, Values.getFloat(request.getParameter("L"+leId), null)); 192 String[] extra = request.getParameter("L"+leId).split(":"); 193 Float usedQuantity = Values.getFloat(extra[0], null); 194 int arrayIndex = Values.getInt(extra[1], 1); 195 creationEvent.addSource(le, usedQuantity); 196 creationEvent.setSourceGroup(le, arrayIndex); 191 197 } 192 198 } -
trunk/www/views/hybridizations/list_hybridizations.jsp
r4003 r4131 253 253 exportable="true" 254 254 show="always" 255 /> 256 <tbl:columndef 257 id="numArrays" 258 property="numArrays" 259 datatype="int" 260 title="Arrays/slide" 261 sortable="true" 262 filterable="true" 263 exportable="true" 255 264 /> 256 265 <tbl:columndef … … 533 542 onclick="itemOnClick(<%=writePermission ? "event" : null%>, <%=itemId%>)" 534 543 title="<%=tooltip%>"><%=name%></div></tbl:cell> 544 <tbl:cell column="numArrays"><%=item.getNumArrays()%></tbl:cell> 535 545 <tbl:cell column="labeledExtracts"> 536 546 <% … … 539 549 { 540 550 String separator = ""; 551 boolean isMultiArrayHyb = item.getNumArrays() > 1; 541 552 for (LabeledExtract le : labeledExtractQuery.list(dc)) 542 553 { … … 552 563 } 553 564 out.write(separator); 565 if (isMultiArrayHyb) 566 { 567 out.write(creationEvent.getSourceGroup(le) + ": "); 568 } 554 569 if (mode.hasPropertyLink()) 555 570 { -
trunk/www/views/hybridizations/view_hybridization.jsp
r4003 r4131 266 266 </tr> 267 267 <tr> 268 <td class="prompt">Arrays</td> 269 <td><%=hyb.getNumArrays()%></td> 270 </tr> 271 <tr> 268 272 <td class="prompt">Array slide</td> 269 273 <td><base:propertyvalue item="<%=hyb%>" property="arraySlide" /></td> … … 317 321 columns="all" 318 322 > 323 <tbl:columndef 324 id="arrayNum" 325 title="Array" 326 show="<%=hyb.getNumArrays() > 1 ? "always" : "never"%>" 327 /> 319 328 <tbl:columndef 320 329 id="name" … … 342 351 %> 343 352 <tbl:row> 353 <tbl:cell column="arrayNum"><%=creationEvent.getSourceGroup(item)%></tbl:cell> 344 354 <tbl:cell column="name"><base:icon 345 355 image="deleted.gif" -
trunk/www/views/rawbioassays/edit_rawbioassay.jsp
r4093 r4131 303 303 return false; 304 304 } 305 else if (!Numbers.isInteger(frm.arrayNum.value)) 306 { 307 alert("'" + frm.arrayNum.value + "' is not a valid number"); 308 frm.arrayNum.focus(); 309 return false; 310 } 311 else if (parseInt(frm.arrayNum.value) <= 0) 312 { 313 alert("Array index must be > 0"); 314 frm.arrayNum.focus(); 315 return false; 316 } 305 317 return true; 306 318 } … … 617 629 value="<%=HTML.encodeTags(name)%>" 618 630 size="40" maxlength="<%=RawBioAssay.MAX_NAME_LENGTH%>"></td> 631 </tr> 632 <tr> 633 <td class="prompt">Array index</td> 634 <td><input <%=requiredClazz%> type="text" name="arrayNum" 635 value="<%=rawBioAssay == null ? Values.getString(cc.getPropertyValue("arrayNum"), "1") : rawBioAssay.getArrayNum()%>" 636 size="12" maxlength="10" onkeypress="return Numbers.integerOnly(event)"></td> 619 637 </tr> 620 638 <tr> -
trunk/www/views/rawbioassays/index.jsp
r4093 r4131 265 265 rba.setName(Values.getStringOrNull(request.getParameter("name"))); 266 266 rba.setDescription(Values.getStringOrNull(request.getParameter("description"))); 267 rba.setArrayNum(Values.getInt(request.getParameter("arrayNum"), 1)); 267 268 int protocolId = Values.getInt(request.getParameter("protocol_id"), -1); 268 269 if (protocolId >= 0) // < 0 = denied or unchanged -
trunk/www/views/rawbioassays/list_rawbioassays.jsp
r4003 r4131 307 307 /> 308 308 <tbl:columndef 309 id="arrayNum" 310 property="arrayNum" 311 datatype="int" 312 title="Array index" 313 sortable="true" 314 filterable="true" 315 exportable="true" 316 /> 317 <tbl:columndef 309 318 id="hasData" 310 319 property="hasData" … … 623 632 enableEditLink="<%=mode.hasEditLink()%>" 624 633 enablePropertyLink="<%=mode.hasPropertyLink()%>" 625 /></tbl:cell> <tbl:cell column="rawDataType"><%=HTML.encodeTags(rdt.getName())%></tbl:cell> 634 /></tbl:cell> 635 <tbl:cell column="rawDataType"><%=HTML.encodeTags(rdt.getName())%></tbl:cell> 636 <tbl:cell column="arrayNum"><%=item.getArrayNum()%></tbl:cell> 626 637 <tbl:cell column="hasData"><%=item.hasData()%></tbl:cell> 627 638 <tbl:cell column="scan" -
trunk/www/views/rawbioassays/view_rawbioassay.jsp
r4003 r4131 297 297 <td class="prompt">Raw data type</td> 298 298 <td><%=HTML.encodeTags(rawDataType.getName())%></td> 299 </tr> 300 <tr> 301 <td class="prompt">Array index</td> 302 <td><%=rawBioAssay.getArrayNum()%></td> 299 303 </tr> 300 304 <tr> -
trunk/www/views/scans/view_scan.jsp
r4003 r4131 325 325 title="Type" 326 326 /> 327 <tbl:columndef 328 id="arrayNum" 329 title="Array index" 330 /> 327 331 <tbl:columndef 328 332 id="software" … … 356 360 /><%=Base.getLinkedName(ID, item, false, true)%></tbl:cell> 357 361 <tbl:cell column="type"><%=HTML.encodeTags(item.getRawDataType().getName())%></tbl:cell> 362 <tbl:cell column="arrayNum"><%=item.getArrayNum()%></tbl:cell> 358 363 <tbl:cell column="software"><base:propertyvalue item="<%=item%>" property="software" /></tbl:cell> 359 364 <tbl:cell column="data"><%=item.hasData() ? "yes (" + item.getSpots() + " spots)" : "no"%></tbl:cell>
Note: See TracChangeset
for help on using the changeset viewer.